rewrite never type documentation#158370
Conversation
| /// // `x` has type `u32` | ||
| /// Some(x) => x, | ||
| /// // `return` has type `!`, which is then coerced to `u32`, | ||
| /// // allowing the `match` to typecheck |
There was a problem hiding this comment.
| /// // allowing the `match` to typecheck | |
| /// // allowing the `match` to pass type checking. |
There was a problem hiding this comment.
Hm, I prefer "to typecheck", do you think this is too jargony?
There was a problem hiding this comment.
Ehhhh vague hand gestures only kind of. As a general matter of how I see people failing to understanding things, I think that programming jargon often has what I call a “noun pileup” problem: squeezing all the meaning into a single noun phrase (or verb, in this case) that lacks any structure to indicate what its parts are referring to if you don’t already know. This suggestion was an attempt, not to fix a definite problem, but to improve the average.
In particular, it add an indication (“pass”) that “type checking” is something done to the match, whereas “typecheck” as a verb whose subject is match could point towards more incorrect theories such as “the match is (at run time) checking that the type of the arm matches the type that is needed”. Does that disagree with how Rust works in general? Yes. But the people who need this documentation won’t necessarily know that, so adding more clues towards the right reading helps learning go more smoothly.
But, just like I said for the rest of these, feel free to disregard this suggestion. It’s a guess at what will be more clear.
There was a problem hiding this comment.
If I can throw my two cents in, I think "pass type checking" reads smoother, something about "...allowing the match to typecheck." just feels clunky.
There was a problem hiding this comment.
To me, "typecheck" is an action done by the compiler, not the code itself. So I suggest "pass typechecking".
Co-authored-by: Kevin Reid <kpreid@switchb.org>
There was a problem hiding this comment.
Due to the slightly messy diff, I ended up splitting my review up into many small comments, so apologies for the large number of them. Most are copy-edits, with a couple small content changes here and there. Feel free to use them or not as you see fit.
| /// `!` is the canonical uninhabited type. `!` represents the type of diverging computations -- | ||
| /// computations which never resolve to any value. |
There was a problem hiding this comment.
| /// `!` is the canonical uninhabited type. `!` represents the type of diverging computations -- | |
| /// computations which never resolve to any value. | |
| /// `!` represents the type of diverging computations -- computations which never resolve to any | |
| /// value. It the canonical uninhabited type. |
| /// A different way to look at it is that since `!` is uninhabited, since it has no values, it is a | ||
| /// marker for unreachable code. |
There was a problem hiding this comment.
| /// A different way to look at it is that since `!` is uninhabited, since it has no values, it is a | |
| /// marker for unreachable code. | |
| /// Another perspective is that since `!` has no values (since it is uninhabited), it is a marker | |
| /// for unreachable code. |
| /// For example, the [`exit`] function is defined as returning `!`, to signify that it doesn't | ||
| /// return normally and exits the process instead. Thus, any code following a call to [`exit`] is | ||
| /// unreachable. ([`panic!`] works the same way.) |
There was a problem hiding this comment.
| /// For example, the [`exit`] function is defined as returning `!`, to signify that it doesn't | |
| /// return normally and exits the process instead. Thus, any code following a call to [`exit`] is | |
| /// unreachable. ([`panic!`] works the same way.) | |
| /// For example, the [`exit`] function is defined as returning `!`, to signify that it exits the | |
| /// process instead of returning normally. Thus, any code following a call to [`exit`] is | |
| /// unreachable. The [`panic!`] macro works the same way. |
| /// Similarly, [`return`], [`break`], [`continue`], [`become`], and infinite [`loop`] expressions | ||
| /// all have type `!`, as the code following them is unreachable. |
There was a problem hiding this comment.
Should become be included here if it is still unstable?
| /// Both match arms must produce values of type [`u32`], but since `break` never produces a value | ||
| /// at all we know it can never produce a value which isn't a [`u32`]. This illustrates another | ||
| /// behavior of the `!` type - expressions with type `!` will coerce into any other type. | ||
| /// The `let` is pointless, but shows that `return` has type `!`. |
There was a problem hiding this comment.
| /// The `let` is pointless, but shows that `return` has type `!`. | |
| /// The `let` binding above is pointless, but shows that `return` expressions have type `!`. |
| /// # } | ||
| /// impl Debug for ! { | ||
| /// fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
| /// fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { |
There was a problem hiding this comment.
| /// fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { | |
| /// fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { | |
| /// // we can dereference `self` (which has type `&!`), | |
| /// // and then coerce to `fmt::Result` |
Moved the sentence below into the example as a comment.
| /// Since `!` has no values, it has no default value either. There is no meaningful implementation | ||
| /// for `default`. Since it would have to return `!`, it would also need to diverge. While one | ||
| /// *could* implement it using `panic!` or an infinite loop, or something alike, that would not be | ||
| /// useful. |
There was a problem hiding this comment.
| /// Since `!` has no values, it has no default value either. There is no meaningful implementation | |
| /// for `default`. Since it would have to return `!`, it would also need to diverge. While one | |
| /// *could* implement it using `panic!` or an infinite loop, or something alike, that would not be | |
| /// useful. | |
| /// Since `!` has no values, it has no default value either. There is no meaningful implementation | |
| /// for `default`, since it would have to return `!` -- in other words it would need to diverge. | |
| /// While one *could* write an implementation using `panic!` or an infinite loop, or something | |
| /// alike, that would not be useful. |
| /// This is because `impl Trait` is not a specific type in and of itself, but instead a marker that | ||
| /// the return type of the function is hidden and the only thing that can be assumed by the caller | ||
| /// is that whatever the type is, it implements `Trait`. |
There was a problem hiding this comment.
| /// This is because `impl Trait` is not a specific type in and of itself, but instead a marker that | |
| /// the return type of the function is hidden and the only thing that can be assumed by the caller | |
| /// is that whatever the type is, it implements `Trait`. | |
| /// Here, never-to-any coercion fails because `impl Trait` is not itself a concrete type, but rather | |
| /// a way to tell the compiler that a function's return type is hidden, and the only thing which can | |
| /// be assumed about this type is that it implements `Trait`. |
| /// In case of using `todo!()` the hidden return type is inferred to be `!`, which may not | ||
| /// implement the trait. |
There was a problem hiding this comment.
| /// In case of using `todo!()` the hidden return type is inferred to be `!`, which may not | |
| /// implement the trait. | |
| /// The compiler then attempts to choose such a concrete type, but is unable to do so. In this case, | |
| /// we abandon the coercion and fall back to just the never type itself, which will fail to compile | |
| /// if `!` does not implement `Trait`. |
I think fallback should be explicitly mentioned here.
| /// // Enum with no variants is an example of an uninhabited type | ||
| /// enum Void {} |
There was a problem hiding this comment.
| /// // Enum with no variants is an example of an uninhabited type | |
| /// enum Void {} | |
| /// // An enum with no variants is an example of an uninhabited type | |
| /// enum Void {} |
Probably this should be moved up above the definition of Onomatopoeias
With the never type stabilization approaching, I think the documentation could be improved a lot over the status quo ^^'
I intend to merge this after #155499. Split it off into a separate PR for ease of reviewing.