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
43 changes: 38 additions & 5 deletions library/std/src/primitive_docs.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#[doc(primitive = "bool")]
#[doc(alias = "true")]
#[doc(alias = "false")]
//
/// The boolean type.
///
/// The `bool` represents a value, which could only be either `true` or `false`. If you cast
Expand All @@ -12,8 +11,8 @@
/// `bool` implements various traits, such as [`BitAnd`], [`BitOr`], [`Not`], etc.,
/// which allow us to perform boolean operations using `&`, `|` and `!`.
///
/// `if` always demands a `bool` value. [`assert!`], being an important macro in testing,
/// checks whether an expression returns `true`.
/// `if` always demands a `bool` value. [`assert!`], which is an important macro in testing,
/// checks whether an expression returns `true` and panics if it isn't.
///
/// ```
/// let bool_val = true & false | false;
Expand Down Expand Up @@ -194,14 +193,48 @@ 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 many types implement `Add<u32>`. However, in the second example,
/// the `else` branch returns a `0`, which the compiler infers from the return type to be of type
/// `u32`. Since `u32` is a concrete type, `!` can and will be coerced to it. See issue [#36375] for more
/// 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;
/// # fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result;
/// # }
Comment on lines 236 to 238
Copy link
Member Author

Choose a reason for hiding this comment

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

Why is Debug defined here instead of imported?

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm going to delete this redefinition and import it instead.

Copy link
Member Author

Choose a reason for hiding this comment

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

Looks like it's because of the orphan rule or something, so I had to revert my change :(

/// impl Debug for ! {
/// fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
Expand Down