3 ms·
No I’m not talking about runtime conversions. I’m talking about conversions that happen at type inference time. Rust is not a subtyping based language, except
by kccqzy 29d ago
No I’m not talking about runtime conversions. I’m talking about conversions that happen at type inference time.
Rust is not a subtyping based language, except for traits and lifetimes. So statements like never being at the bottom of the type hierarchy is irrelevant here even though it is correct. If Rust had higher rank types the never type is also (forall a. a) but still it doesn’t matter. It is simply surprising for a type to be converted implicitly according to subtyping rules other than for traits and lifetimes.
- SabrinaJewson 29d agoDo you have an example of a piece of code that behaves in a surprising way because of this rule?
- kccqzy 29d agoI don’t need to write examples because the article has plenty. All the fixes that Waffle needs to fix are precisely the code that behaves in a surprising way.
- kibwen 29d agoNone of this has anything bad to say about coercion or fallback, it's a consequence of the fact that Rust is an expression-oriented language which had expressions (like `loop {}`) which logically evaluated to the never type when in return position, and yet did not have the machinery in place to support it as a proper concept anywhere outside of return position, and so they chose the unit type as a relatively benign alternative in those contexts, which caused no problems whatsoever until the day came when they decided to actually implement the never type.
- kccqzy 28d ago> so they chose the unit type as a relatively benign alternative in those contexts This is what the article defines as fallback. So the issue has everything to do with fallback.
- kibwen 29d agoLet's avoid using the term "subtyping", which as you say is irrelevant here. The reason you need diverging functions to satisfy arbitrary type obligations (i.e. to coerce to any other type) is because otherwise anything as simple as `let x = Some(42); x.unwrap();` just completely fails to compile, because `unwrap` is internally just: fn unwrap<T>(t: Option<T>) -> T { match t { Some(foo) => foo, None => panic!() } } ...and this function couldn't otherwise typecheck because it doesn't return a `T` in the `None` branch. You need coercion here.
- kccqzy 29d agoNo you don’t need coercion. You only need polymorphism. The type of `panic!()` could be an arbitrary U, which unifies just fine with the type T here. Generally languages with such polymorphism have a never type only because they don’t also support impredicative polymorphism.
- kibwen 29d agoAnd then once you have `fn foo<T>() -> T`, what do you write in the body that allows it to typecheck?
- kccqzy 28d agoYou still write `panic!()`. It type checks using polymorphism only, without any coercions.
- kibwen 28d agoBut `panic!()` is just a macro invocation that needs to expand to something, and the question here is what that something ought to be in order to produce a valid program. Currently it expands to this: https://github.com/rust-lang/rust/blob/98fd715edd3a0a5aa8f2041d7e20a3143b0fc0d3/library/core/src/panicking.rs#L138 https://github.com/rust-lang/rust/blob/98fd715edd3a0a5aa8f20... , which is a function with a return type of `!`, which has indicated a diverging function since long before Rust even considered having a first-class never type.