Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add info about ! and impl Trait #76099

Merged
merged 13 commits into from
Sep 2, 2020
41 changes: 36 additions & 5 deletions library/std/src/primitive_docs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,15 +194,46 @@ mod prim_bool {}
/// # `!` and traits
///
/// When writing your own traits, `!` should have an `impl` whenever there is an obvious `impl`
/// which doesn't `panic!`. As it turns out, most traits can have an `impl` for `!`. Take [`Debug`]
/// which doesn't `panic!`. The reason is that functions returning an `impl Trait` where `!`
/// does not have an `impl` of `Trait` cannot diverge as their only possible code path. In other
/// words, they can't return `!` from every code path. As an example, this code doesn't compile:
///
/// ```compile_fail
/// use core::ops::Add;
///
/// fn foo() -> impl Add<u32> {
/// unimplemented!()
/// }
/// ```
///
/// But this code does:
///
/// ```
/// use core::ops::Add;
///
/// fn foo() -> impl Add<u32> {
/// if true {
/// unimplemented!()
/// } else {
/// 0
/// }
/// }
/// ```
///
/// The reason is that, in the first example, there are many possible types that `!` could coerce
/// to, because the function can return one of many concrete types. However, in the second example,
camelid marked this conversation as resolved.
Show resolved Hide resolved
/// the `else` branch returns a `0`, which the compiler infers from the return type to be of type
/// `u32`, which is a concrete type that `!` can be coerced to. See issue [#36375] for more
camelid marked this conversation as resolved.
Show resolved Hide resolved
camelid marked this conversation as resolved.
Show resolved Hide resolved
/// information on this quirk of `!`.
///
/// [#36375]: https://github.com/rust-lang/rust/issues/36375
///
/// As it turns out, though, most traits can have an `impl` for `!`. Take [`Debug`]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most -> all? Not sure if that's true but it seems like it should be.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's what I thought too; I would think you would be able to impl any trait for ! since it can never :) exist. The body of it could just be panic!("This shouldn't happen!").

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or even simpler, *self, yeah.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean because it can be coerced to any concrete type, so it can just return itself?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-> impl Trait is not allowed in trait methods. So any trait it implemented would require returning some concrete type. Since ! can be coerced to any type, it would be coerced to the required type.

/// for example:
///
/// ```
/// #![feature(never_type)]
/// # use std::fmt;
/// # trait Debug {
/// # fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result;
/// # }
/// # use std::fmt::{self, Debug};
/// impl Debug for ! {
/// fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
/// *self
Expand Down