More natural optional some and any types. When writing an optional some or any type, you no longer have to wrap the type in parentheses. Instead of (some Rocket)?, you can simply write some Rocket? (SE-0521).
It's fun to follow Swift. As a fan of Rust, Swift feels similarly modern, while being significantly less principled, and more open to adding all sorts of kitchen sink convenience features.
(See also, the whole issues caused by adopting type inference and subtyping, causing exponential blowup and time outs on type checking one line programs.)
I am really happy that Rust avoided the fate so far, but it's also fun to have a point of comparison.
Sigh, I got Anubis choosing to eat my comment, so this is very very abbreviated
The core difference is overloading on return type. More or less every other language disallows this - there is a single return type for any given parameter types. this applies to generic functions as well: even though different instantiations of a function may have different return types, there's only a single instantiation of the function (and thus return type) for a given set of parameter types.
e.g.
fn foo(arg:u32)->u32;
fn foo(arg:u32)->String;
is not valid, but in swift
func foo(arg:Int)->Int { return arg; }
func foo(arg:Int)->String { return String(arg); }
let x = foo(1) + 1; // x is int
let y = foo(2) + "hello"; // y is String
let z = foo(arg:foo(arg:1)); // ambiguous
let z1 = foo(arg:foo(arg:1)) + 1; // it's an Int
let z2 : Int = foo(arg:foo(arg:1)); // it's an Int
let z3 = foo(arg:foo(arg:1)) + "hello"; // it's a string
let z4 : String = foo(arg:foo(arg:1)); // it's a string
You can see how combining these in different ways could start leading to a very large set of possible call structures
(Disclaimer: I am not trying to evangelize Rust here. I don't say Rust is better than Swift.)
The core difference is overloading on return type.
I don't think that can be so, because Rust is also one of the unusual languages to allow overloading on return type, although Rust makes overloading of any kind clunky to discourage using it heavily.
core::convert::Into::into is an example of a function in the Rust standard library that is overloaded on its return type and typically is used with the return type inferred, as x.into() with some arbitrary x.
Rust rejects (refuses to infer types for) each of the foo(...) + ... expressions, but wrapping the addition operations in more explicit function calls bypasses this.
@5d22b Ah right - this took me a moment to recall: rust does not allow ad hoc overloading.
e.g. I forgot entirely that rust does not actually allow
fn foo(arg:u32)->u32;
fn foo(arg:f32)->f32;
It took me some futzing around on godbolt to restore my understanding of all of this.
So something like into() has a single definition, if we look at a generic function I think this is equivalent to something like fn thing<T>(arg:i32)->T where you can then have foo(thing(arg:1)) and there is a single type for foo, so there's a single candidate you're trying to bind T to. But in swift you can have multiple definitions of foo that have different parameters types so there are multiple different inference paths. e.g func thing<T>(arg:i32)->T but also distinct func thing(arg:Int)->Float, and func thing(arg:String)->Blargh.
The core issue is that rust only allows a single function named F with a single type, whereas swift allows many.
So when rust says "what is the type of F?" there is one answer, for swift there can be multiple.
I'm aware, yeah. The situation of why Swift ran into that issue is a bit more complicated, I didn't want to retell it here.
I think when I researched it I viewed it as a problem arising from HMTI (so some sort of constraint solving) + a situation where the compiler is allowed to pick "one out of several options" at many nodes in the graph, resulting in it having to explore exponentially many paths. Subtyping and implicit conversions make this really easy iirc.
If I understand what you mean, Swift also restricts type inference within function boundaries—after func, you must specify the types of all terms in the signature. The blowup comes from the search branching factor when we have type-distinguished overloading, especially of operators, which are free functions with common names. I think Rust was considering adding that feature, and if so I might warn against it.
mond | 9 hours ago
It's fun to follow Swift. As a fan of Rust, Swift feels similarly modern, while being significantly less principled, and more open to adding all sorts of kitchen sink convenience features.
(See also, the whole issues caused by adopting type inference and subtyping, causing exponential blowup and time outs on type checking one line programs.)
I am really happy that Rust avoided the fate so far, but it's also fun to have a point of comparison.
5d22b | 9 hours ago
Rust has both type inference and subtyping too :-) but restricts them (function-local type inference, subtyping of references).
olliej | 5 hours ago
Sigh, I got Anubis choosing to eat my comment, so this is very very abbreviated
The core difference is overloading on return type. More or less every other language disallows this - there is a single return type for any given parameter types. this applies to generic functions as well: even though different instantiations of a function may have different return types, there's only a single instantiation of the function (and thus return type) for a given set of parameter types.
e.g.
fn foo(arg:u32)->u32; fn foo(arg:u32)->String;
is not valid, but in swift
You can see how combining these in different ways could start leading to a very large set of possible call structures
5d22b | an hour ago
(Disclaimer: I am not trying to evangelize Rust here. I don't say Rust is better than Swift.)
I don't think that can be so, because Rust is also one of the unusual languages to allow overloading on return type, although Rust makes overloading of any kind clunky to discourage using it heavily.
core::convert::Into::intois an example of a function in the Rust standard library that is overloaded on its return type and typically is used with the return type inferred, asx.into()with some arbitraryx.Here is your return type overloading example converted to Rust('s clunky overloading), runnable in the Rust Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=1591c1e8e4634aa2e5dfc2eae3408840
Rust rejects (refuses to infer types for) each of the
foo(...) + ...expressions, but wrapping the addition operations in more explicit function calls bypasses this.olliej | 34 minutes ago
Oh right, that's how .into() works - please hold while I work out the exact problem.
I wonder if it's specific knowing at least one side statically. Please hold.
olliej | 10 minutes ago
@5d22b Ah right - this took me a moment to recall: rust does not allow ad hoc overloading.
e.g. I forgot entirely that rust does not actually allow
It took me some futzing around on godbolt to restore my understanding of all of this.
So something like into() has a single definition, if we look at a generic function I think this is equivalent to something like
fn thing<T>(arg:i32)->Twhere you can then havefoo(thing(arg:1))and there is a single type forfoo, so there's a single candidate you're trying to bindTto. But in swift you can have multiple definitions offoothat have different parameters types so there are multiple different inference paths. e.gfunc thing<T>(arg:i32)->Tbut also distinctfunc thing(arg:Int)->Float, andfunc thing(arg:String)->Blargh.The core issue is that rust only allows a single function named F with a single type, whereas swift allows many.
So when rust says "what is the type of F?" there is one answer, for swift there can be multiple.
olliej | 6 minutes ago
Oh, to put this more clearly, you can do:
but consider how that would resolve if you could also have
now
someInfo.into()can't be inferred.mond | 8 hours ago
I'm aware, yeah. The situation of why Swift ran into that issue is a bit more complicated, I didn't want to retell it here.
I think when I researched it I viewed it as a problem arising from HMTI (so some sort of constraint solving) + a situation where the compiler is allowed to pick "one out of several options" at many nodes in the graph, resulting in it having to explore exponentially many paths. Subtyping and implicit conversions make this really easy iirc.
[OP] kevinc | an hour ago
If I understand what you mean, Swift also restricts type inference within function boundaries—after func, you must specify the types of all terms in the signature. The blowup comes from the search branching factor when we have type-distinguished overloading, especially of operators, which are free functions with common names. I think Rust was considering adding that feature, and if so I might warn against it.