Nullable<T> instead of Option<T>

To me it is strange to name something "null" that is not a pointer or an ascii char. Some/None is much more descriptive to what the type conceptionally is while Null/Value requires some understanding of what these terms mean in other languages.

Anyways, even if it would have been a better idea to name it Nullable in the first place there will certainly neither be a rename nor an alias for the reasons others have stated above.

16 Likes

This depends on the background of the person.

I started to learn Rust after some years using Scala. Scala uses the same nomenclature (Option/Some/None), and it is heavily used in the standard library, so Option in Rust was pretty obvious for me. I guess that this should common for people with experience in other ML-based languages.

Also, there are a few cases where Option is a better name than Nullable. For example, std::ptr::NonNull::new returns an Option<NonNull<T>>. A type like Nullable<NonNull<T>> can be confusing.

9 Likes

The languages that use "null" use it with reference/pointer types and inherit "null" from the C concept of a null pointer (literally a pointer whose value is 0 when coerced to an integer). Java and C# have null references, but non-reference values cannot be null in either language.

In JavaScript anything can be null (or undefined…) but honestly, JavaScript is at best an example on how not to design a language. Just because it's popular does not mean that other languages should imitate it, because its design is definitely not the reason it's popular.

Scala has Option with Some/None just like Rust. Java (since Java 8) has Optional and uses the terms "present" and "empty". C++ (since C++17) has optional, derived from boost::optional which dates back to 2003! I'm not sure whether the original author of boost::optional was inspired by ML's Option or came up with the name independently.

In any case, there are many popular languages using the same nomenclature, so

In my opinion, there is zero chance someone who has not read the rust documents can tell what this function does.

is most definitely incorrect (and is not an opinion at all but an assertion that is either true or false, in this case false.)

6 Likes

IMHO that looks awful, but I’m not here to tell my opinion. I’m here to tell you that Option doesn’t make values nullable (in most cases, applies a byte to the beginning instead). I once implemented a nullable type in Rust, and it was completely unsound. There’s a thing, of course, called NPO (Null Pointer Optimization, not related to pointers though), but it’s only actual while talking about NonZero-types. What I’m trying to say is that even if we changed name for the type, it would be completely wrong - either to use such a name or to use such a type.

2 Likes

There's basically zero chance of Rust renaming one of its most commonly used types at this point, so I don't think it's even worth arguing about.

Even if some new name was way better, it's not going to happen. Such change would cause too much churn for existing users, updates of codebases, documentation, books, Q&A, etc. Editions and automated migration tools are still not an excuse for needless churn.

10 Likes

If Null is confusing, maybe we can use Nilable and Nil? It would be readable, since nil is being used in Math literature. I don't like Option, because to me it is like we are talking about offering multiple choices and someone wants to select one, while we are talking about a value that may be absent or void.

Actually optional is a much better name.(present and empty as well) However, I didn't want to mention that because I still think nullable or nillable or similar names would be better choices because they directly state what the purpose of the type is.

That was informal speaking. I didn't really mean "zero chance", I meant the chance is not so high. It's like when you say "I'm so hungry I can eat a cow". That's just speaking.

Unfortunately the forum does not allow me to make more replies, so I have to edit this post. Thank you so much for your answers. I'm not really saying that this name must change or there is an urgent need for doing that. I tried my best to clarify that in my initial post. I just wanted to share my opinion and just suggest a change (if was possible). Also I wanted to know if there is some reasoning behind this naming and if that would be a good idea to use a different name like nullable for a similar construct in some other special purpose language.

@aybehrouz If your main goal here is to actually get Option renamed or add an alias for it that you consider more readable, then as a member of libs-api, I can just tell you that it isn't going to happen. I suspect I wouldn't be alone in objecting to such a change/addition to the standard library. So if that is indeed your goal, then I can just save you some time and put the kibosh on it right now.

You could in theory suggest a language level change, and that would come under the jurisdiction of the lang team, of which I am not a member. While I can't speak for them, I am just about as similarly certain that they wouldn't accept such a change either.

If you're just trying to noodle about what would have been a better name if we could go back in time, then that's a different story. If that is your goal, it would be good to clarify that.

16 Likes

null works the same in JavaScript as it does in Java: references can be null, values can't be null. The difference is that everything is (logically) boxed in JavaScript (and similarly in Python) whereas in Java some types are unboxed.

2 Likes

So you're saying that Option to you means some enumeration where you choose between N different values, e.g. a species drop-down between Human, Elf, Orc, Gnome, Other.

The main problem with Nullable is the question of what does Nullable<Nullable<T>> mean? Is it still just a choice between T or null, or is it a choice between T, null, and a different null?

Option<T> is a simple enum { Some(T), None }, and as such Option<Option<T>> is a simple composition and obviously has three possible states, Some(Some(T)), Some(None), and None.

I think a core misunderstanding here might be that this sentence is completely nonsensical in Rust. There aren't any "null values" in safe Rust the way there are in Java or other OOP-heavy languages. The lack or presence of Option isn't like a @Nonnull or @Nullable annotation. Values aren't manipulated via handles which could be null, they're used directly. If you wrap a type in Option, it takes up more space.

The "null pointer optimization" is exactly that, an optimization. It's guaranteed that if you have Option<&T>, that this has the same representation as a nullable pointer. But this is a completely irrelevant detail to 99% of Rust programs, which only care that Option<X> is either some value X or no value. That when the X value is &T the Option is no larger than the nonoptional value doesn't matter except as an optimization or when doing external FFI with languages other than Rust.


Of the things someone learning Rust needs to learn, the naming of Option is among the least impactful. Learning a language always means learning what names it uses to refer to things, but Rust has much larger differences than just naming. Rust isn't an OOPy language, and trying to apply OOPy thinking to it will only land you in trouble. For better or worse, Rust encourages (and essentially requires) a very different ("data oriented") way of thinking about programming from traditional OOP schools of thought. To that end, forcing people out of that OOPy comfort zone early could even be considered beneficial.

Reversing the order of if let to flow left to right has been discussed as a possibility. It is very similar to why .await is postfix, as well. I don't think it's all that likely (assigning names is important to surrounding code, justifying the prefix signaling), but a postfix match is somewhat likely to happen eventually. With that you'd write

config_max.match {
    Some(max) => println!("The maximum is configured to be {max}"),
    _ => println!("No maximum is configured"),
};

and I guess there's a rather unlikely possibility that we'd eventually extend that to allow

config_max.match Some(max) {
    println!("The maximum is configured to be {max}");
} else {
    println!("No maximum is configured");
}
6 Likes

I think the only way for this status quo to change is to introduce a language-level concept of optional/nullable types, maybe writing i32? as a type instead of Option<i32>. This would be legitimately a cool addition but people would still need to refer to Option for a number of reasons, if only because there's years of Rust using this type.


About the if .. match .. it looks interesting, but with semantics inverted from the traditional match (match x normally matches on x, this if config_max match x matches on config_max!)

1 Like

While shorthand for Option types might be possible as a lang change (though I suspect it wouldn't happen, personally), that still wouldn't change the names Option nor None.

(And I can confirm that, as @burntsushi hypothesized, the lang team would not add a feature just to rename Option either -- especially not over the objections of libs-api, though I don't think that would ever even come up since that's their turf and lang has no desire to try to contest it.)

2 Likes

It seems like you're focusing on the technical questions like "how would it be possible to change this". When it comes to Rust, it helps to also look at the social question: "would it be possible to make a case for changing this", "what would the impact on the ecosystem be", etc.

For those, @burntsushi and several others already gave the clear answer: this won't happen. Incompatible, too disruptive, little benefit, etc.

Even if you could make a case that there's an objectively better answer (rather than just one that's subjectively better for people with different programming experiences and familiar with other languages), that would only be an answer for "what might we have done differently pre-1.0", not something we'd change today.

It's sometimes useful to think about what we could have done differently pre-1.0, because sometimes that can inspire possibilities that remain possible today. But "name this differently" is rarely (not never, but rarely) one of those cases.

10 Likes

To be honest, I think that is the case with an increasing amount of content on IRLO. There are things being posted that, with 30 seconds of thinking, clearly have no possibility of happening, even over an edition. It's my personal preference to point this out when threads go on seemingly forever, and that's normally the end of it. I wish they were just closed, though.

9 Likes

I would feel bad cutting anyone off like that, unless the discussion becomes problematic in tone. I do agree that it’s healthy to be clear about the prospects of such proposals though. Beyond that, anyone can decide for themselves whether they want to participate in any ongoing side-discussions.

Notably, if the goal is no longer to change anything about the language, we are less clearly in IRLO territory. Instead of internals.rust-lang.org, it’s quite reasonable to choose users.rust-lang.org for a place of discussion for exploring the history of API naming, or personal preferences in surface-level syntax of programming languages.

As for this thread, I believe that just about everything regarding the original question of the naming of Option<T> in particular has already been said, so I don’t see much value in leaving the topic open for as long as another 3 months either. New tangent discussions at a later point can always happen elsewhere.

Changing topic auto-close timer to “48h after the last reply”.

7 Likes

On the other hand, the unfamiliarity of the first form tells you to look at the documentation to make sense of it, because it doesn't look like the @Nullable annotation in Java, or the C# Nullable type, but is instead different. And once you learn a bit more about Rust, you learn why it's different, and also why Option<i32> in Rust is not the same as @Nullable int in Java, or C# Nullable<int> (lack of pervasive "hidden" null references).

Every language has a "weirdness" budget, and one of the best places to spend weirdness is being weird where the underlying concept differs to what a programmer used to a "normal" language would expect if you tried to use the "normal" names.

5 Likes

I apologize for not participating in the conversation, the forum was not letting me post for 16 hours.

It's interesting for me to know why some people say technically this is not possible. I do not exactly know how the Rust compiler works. Anyway, for a compiler, usually you should be able to define an alias for any entity in a compilation unit. For example you say I'd like to use Nullable instead of Option. Compiler will always know that in your code Nullable means Option. Interestingly you can still use Option without any problems. You can have many aliases for any entity and use them at the same compilation unit.

The only problem that this aliasing introduces is that Nullable will be defined in the scope that Option is defined and you should not try to define that name in that scope again. For that reason, any compilation unit should somehow opt in for using that alias. This can be done by using compilation flags, like language level/version, or just a simple compilation option like: -nicenames, or in a different approach by using compiler directives or language directives inside the compilation unit itself.

There is no need to change the API of any library. A user usually views that API through his IDE or the provided documentations, so we can always show him the appropriate view of the API. For example his IDE shows to him that function do_something returns Nullabe<i32> while it actually returns Option<i32>.

Maybe there is something that prevents Rust compiler from doing this trick, but I do not see any explanations about that here.

I personally think that only if a topic is clearly unrelated to the subject of the forum, it should be closed. I'm OK with closing this topic, but note that closing a topic could be offensive for the creator sometimes. If that's how you manage topics here I would strongly recommend a change in the policies.

They are actually very similar. They are constructs created for dealing with the fact that a value may not be present. Of course they can not behave completely similarly since these are different languages. In C# and Java reference types can be null so we need @NotNull in java or Nullable can only be used for value types in C#. I do not think that a programmer by seeing the same name would expect the exact same behavior and properties, but he would expect the same "concept".

https://doc.rust-lang.org/book/ch06-01-defining-an-enum.html#the-option-enum-and-its-advantages-over-null-values

To clarify, at least for my part, I was not trying to say that your ideas were technically impossible. I was saying that they are impossible because of objections to the idea itself. That is, before you even start to think about the technical solutions to a problem, you have to ask: is this a problem we should solve? What I was saying is that the answer to that, from the perspective of libs-api, is going to be a firm "no." That's well before even getting into the particulars of how you might go about implementing your ideas.

13 Likes

I believe nobody is claiming it’s actually impossible to add an alias. The statements in that direction were likely only concerned with impossibility as a library change without special compiler support (or new general features for defining such aliases).

So you are correct that there’s no hard technical reason against such a change.


The more important reason of course then is that the change is not wanted. Naturally, almost all language changes that are technically possible are not wanted. There’s just so many more (technically possible) possibilities compared the single path of evolution the language will ultimately take, and the way this gets narrowed down is by decisions what is or isn’t wanted.


Edit: Sorry for the thematic overlap/similarity with the previous response, I was formulating this before I’ve seen it :slight_smile:

7 Likes

But that's my whole point - the concept is not the same as Java's @Nullable, or C#'s Nullable<T>, and a programmer who believes that Rust's Option is the same concept as @Nullable or Nullable<T> is going to end up having to relearn a whole bunch of Rust when they realise their mistake.

In Java, all references are implicitly nullable; the @Nullable annotation is used to indicate to static analysis tools that the code consuming the annotated reference is aware that this reference can be null, and will do something sensible (i.e. not trigger a NullPointerException) in that case. Allowing references to be implicitly nullable is what Tony Hoare described as his "billion dollar mistake" - and Rust does not make this specific mistake.

If Rust used the "common" terminology of Nullable<T>, it'd be reasonable for the programmer to assume that Rust handles missing values just like Java (or C#, or ALGOL60, or other languages that have the same issue), and that Nullable is just an annotation like Java's @Nullable or C#'s Nullable<>. They would eventually find out that they're wrong, but in the meantime would be confused by why (e.g.) nobody seems to care that a parameter foo: String might be null. Eventually, someone would correct them, and they'd relearn a pile of things, but that's a lot of their time wasted.

By using weirdness budget on this, Rust gets to catch this misconception early; Option is not a funny way to spell Nullable, but instead a generic type like OCaml's Option or C++ 17 and later's std::optional, and the expectations you have around the rest of the language are thus very different to those that you'd have if you treated it as "in this particular case, I'm ready and able to handle None, but normally I'll just crash at runtime if I get a surprise None value" like Nullable.

And the neat thing about Option is that it's not special in the way that Nullable is. I can define my own enum with two variants, one of which is fieldless, and it will compile to the same representation as Option does, while making my code easier to read, because it's got domain-specific information in it.

enum Destination {
    Unknown,
    GpsCoordinates { latitude: Degrees, longitude: Degrees },
}

is equivalent to (but possibly clearer than):

struct GpsCoordinates {
    latitude: Degrees,
    longitude: Degrees,
}

type Destination = Option<GpsCoordinates>;
4 Likes