Simplifying Optional Type Annotations

struct User {
    id: i32,
    name: String,
    age: u8,
    address: Option<String>,
    wx_token: Option<String>,
}

We can simplify it:

struct User {
    id: i32,
    name: String,
    age: u8,
    address: String?,
    wx_token: String?,
}

This syntax is just a syntactic sugar that compiles to an Option type behind the scenes and is compatible with the existing syntax.

The ? syntax is currently already used for other another purpose: bubbling up error-like values from fns and methods e.g. Error types, but also Option. And if/when the infrastructure for it becomes stable, also for user-defined types. Even if it's grammatically unambiguous (and I'm not convinced of that ATM), I don't believe this kind of conceptual overload is helpful to most people, and instead just sets the stage for confusion in the future.

Your proposal also introduces a special case for optional types, including special syntax. This has not been done in Rust before, and if added its not likely to make things easier. Just shorter, which is absolutely not the same thing.

Another point worth considering is any precedence effect that implementing this proposal might have: there have already been requests for a long time now for new syntax for some special corner of the language, and my response to that is: If that is done, the end game there is Perl, a famously "write-only language". However, if this were implemented, disallowing such things in the future would at the very least feel inconsistent in terms of decisions being made by the rust project as a whole.

18 Likes

Thank you for your answer, but Type? does not conflict with existing usage.

1 Like

"conflict with existing usage" does not just mean that something already has an existing meaning.

We could write [T] as loop T, which is not a type syntax we currently have, but that would still conflict with existing usage because it's confusingly different than what loop means in other contexts.

Having T? mean Option<T> isn't simpler. It's shorter, but that's not the same as being simpler. It hides information from the user, and adds to the surface area of the language.

9 Likes

How would I extend this for symmetry with other uses of ? in Rust? There's an ongoing effort to generalize ? beyond just Option and Result, which would mean that postfix ? becomes a user-extendable operator for any case where you have a "continue" path and a "return early" path, but I can't even see how I'd extend this use of ? to support Result, let alone an arbitrary user-defined type.

Remember that there's only a small number of easily-typed symbols on a typical keyboard, so we have to be careful what we spend them on; we want to spend them on things where their use either makes a significant improvement to the language usability (such as ! for macro invocation, so that it's always obvious when something that looks like a function call isn't one), or where they're so common that using a longer form brings in other usability issues (such as postfix ? for early return on failure, which replaced try! {}).

This results in a very high bar to clear for adding new syntax to shorten the language; we haven't done it for .await, for example, even though that's frequently used in async Rust. Among other issues, we want to be sure that there won't be a future thing that we wish we'd saved this syntax for - or at least that when we find that future thing, we're OK with this syntax having stolen the "best" symbol for it, and can find another symbol.

3 Likes

DIY

type OptStr = Option<String>;

struct Foo {
    f: OptStr
}
3 Likes

I would like to kindly and slightly disagree. The information is not hidden, it is still there. It's just (kind of) prefix Option<> being replaced with postfix ?. The exact same thing happened with try! {} -> ?.

Also, we don't have Reference<T> and you simple &T instead. And ?Sized instead of MaybeNot<Sized>. And *foo among foo.deref(). And even short fn, impl or mod instead of full words.

Rust is not always explicit and being explicit is not only the single best choice. Look at implicit returns, type inference, implicit int types, lifetime elision, default trait methods, deref coercion, and perhaps much more - they all, contrary to Type?, really hide some information to the user and the majority is fine with that. Some of the "implicit features" are even quite surprising (in terms of hiding information) like async that doesn't require -> impl Feature<...> syntax or not yet delivered try blocks.

And finally, Type? is already common in C#, Kotlin, Swift, and TypeScript (with a slightly different syntax)

That being said, I don't have a very strong opinion on Type?. I think I'm slightly in favor of it, but it's not something I would fight for.

1 Like

If Type? where a thing I would expect it to be <Type as Try>::Output to mimic the behaviour of value?.

4 Likes

I feel like the existing use of postfix ? in expressions, which is mainly associated with Result, not Option, even though it can be used with Option, means that reusing unary ? in types as shorthand for Option would be a bad fit for Rust even though it works well in other languages.

I don't have a better idea, but I don't particularly mind long-windedness in type declarations, so I am probably not the right person to think of a better idea.

9 Likes