As we know, the never type (!) can be used as any type in Rust. However it appears that the never type can not satisfy every trait bound, which is counter-intuitive. Is there a reason why the feature is missing in the language?
// does not compile
fn test1() {
trait MyTrait {
fn my_method();
}
fn requires_my_trait<T: MyTrait>(_: T) {}
let never = unreachable!();
requires_my_trait(never);
}
// compiles
fn test2() {
trait MyTrait {
fn my_method();
}
fn requires_my_trait<T: MyTrait>(_: T) {}
impl MyTrait for String {
fn my_method() {
todo!()
}
}
let never = unreachable!();
let as_string: String = never;
requires_my_trait(as_string);
}
// does not compile
fn test3() {
trait MyTrait {
fn my_method();
}
fn requires_my_trait<T: MyTrait>(_: T) {}
impl MyTrait for ! {
fn my_method() {
todo!()
}
}
let never = unreachable!();
requires_my_trait(never);
}
fn main() {
// is trivial enough
let i = default_from_fn(integer);
// what should this be?
let n = default_from_fn(never);
}
fn integer() -> i32 { 32 }
fn never() -> ! { panic!() }
"Easy", you might say. "Let the compiler implement all of its associated members as well".
// not touching any of the associated `fn`s or `const`s;
// only referencing/taking the `TypeId::of` the inner `type`
fn id_from_fn<T: Trait>(f: fn() -> T) -> TypeId {
TypeId::of::<T::Type>()
}
// another regular trait, just
// a bit more feature-full
trait Trait {
fn call(self, val: Self::Type);
const CONST: Self::Type;
type Type: Any;
}
// in our imaginary compiler
// let it, from now on:
impl Trait for ! {
// easy enough
fn call(self, val: Self::Type) { panic!(); }
// `!` coerces into any type, no problem
const CONST: Self::Type = panic!();
// and what should this be?
type Type: Any = ... ?;
}
Should we set the Type to be ! here? The ! itself has a perfectly valid TypeId, after all. Though if the code itself happens to propagate the T::Type further - let's say, by requiring an additional argument of type T::Type at any point, later on it will happily fail with:
expected !, found <type>
(E0308)
mismatched types
expected type `!`
found type `{type}`
(E0308)
Why are we expecting ! instead of a <type>? Have fun tracing the types all the way back to the point where the impl Trait for ! was silently implemented for you, to figure it out. Would it be worth trading this kind of confusion for the tiniest bit of convenience you'd get by having this particular impl done for you, without any conscious intent on your part?
Consider an automatic impl Iterator for !. What would its Item type be? No single choice makes it compatible with every situation.
In general, traits can make many claims about types. Some of them would be incorrect to apply to ! (like Default), and some of them do but the right choices of associated types and constants can't be automated.
Once ! is stable, the right thing to do will be for each trait to provide an implementation for !if possible, but these implementations will not always be boilerplate.
Another problematic case would be implementing both Copy and Drop (which parts of the compiler consider to be mutually exclusive), or more generally anything resulting in a logical contradiction. (Allowing contradictions in e.g. the trait solver can result in soundness bugs.)