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 docs for never primitive #46232

Merged
merged 6 commits into from
Dec 10, 2017
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/librustdoc/clean/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1783,6 +1783,7 @@ pub enum PrimitiveType {
RawPointer,
Reference,
Fn,
Never,
}

#[derive(Clone, RustcEncodable, RustcDecodable, Copy, Debug)]
Expand Down Expand Up @@ -1824,6 +1825,7 @@ impl Type {
RawPointer(..) => Some(PrimitiveType::RawPointer),
BorrowedRef { type_: box Generic(..), .. } => Some(PrimitiveType::Reference),
BareFunction(..) => Some(PrimitiveType::Fn),
Never => Some(PrimitiveType::Never),
_ => None,
}
}
Expand Down Expand Up @@ -1872,6 +1874,7 @@ impl GetDefId for Type {
Primitive(PrimitiveType::Tuple).def_id()
},
BareFunction(..) => Primitive(PrimitiveType::Fn).def_id(),
Never => Primitive(PrimitiveType::Never).def_id(),
Slice(..) => Primitive(PrimitiveType::Slice).def_id(),
Array(..) => Primitive(PrimitiveType::Array).def_id(),
RawPointer(..) => Primitive(PrimitiveType::RawPointer).def_id(),
Expand Down Expand Up @@ -1908,6 +1911,7 @@ impl PrimitiveType {
"pointer" => Some(PrimitiveType::RawPointer),
"reference" => Some(PrimitiveType::Reference),
"fn" => Some(PrimitiveType::Fn),
"never" => Some(PrimitiveType::Never),
_ => None,
}
}
Expand Down Expand Up @@ -1939,6 +1943,7 @@ impl PrimitiveType {
RawPointer => "pointer",
Reference => "reference",
Fn => "fn",
Never => "never",
}
}

Expand Down Expand Up @@ -2873,6 +2878,7 @@ fn build_deref_target_impls(cx: &DocContext,
RawPointer => tcx.lang_items().const_ptr_impl(),
Reference => None,
Fn => None,
Never => None,
};
if let Some(did) = did {
if !did.is_local() {
Expand Down
2 changes: 1 addition & 1 deletion src/librustdoc/html/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -638,7 +638,7 @@ fn fmt_type(t: &clean::Type, f: &mut fmt::Formatter, use_absolute: bool) -> fmt:
fmt::Display::fmt(t, f)?;
primitive_link(f, PrimitiveType::Array, &format!("; {}]", n))
}
clean::Never => f.write_str("!"),
clean::Never => primitive_link(f, PrimitiveType::Never, "!"),
clean::RawPointer(m, ref t) => {
match **t {
clean::Generic(_) | clean::ResolvedPath {is_generic: true, ..} => {
Expand Down
127 changes: 127 additions & 0 deletions src/libstd/primitive_docs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,133 @@
#[stable(feature = "rust1", since = "1.0.0")]
mod prim_bool { }

#[doc(primitive = "never")]
//
Copy link
Member

Choose a reason for hiding this comment

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

Why this line?

Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure why it was there in the first place, but it does match the other primitive docs.

Copy link
Member

@GuillaumeGomez GuillaumeGomez Nov 28, 2017

Choose a reason for hiding this comment

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

We should remove them then!

@rust-lang/cleanup-team

/// The `!` type, also called "never".
Copy link
Member

Choose a reason for hiding this comment

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

This "introduction" sounds strange. I expected to have something after directly but just a dot. So strange.

Copy link
Member

Choose a reason for hiding this comment

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

This kind of introduction line matches the other primitives. Compare with "Raw, unsafe pointers, *const T, and *mut T", "References, both shared and mutable", "A finite heterogeneous sequence, (T, U, ..)", all of which appear in the current docs. Do you have a better suggestion?

///
/// `!` represents the type of computations which never resolve to any value at all. For example,
/// the [`exit`] function `fn exit(code: i32) -> !` exits the process without ever returning, and
/// so returns `!`.
///
/// `break`, `continue` and `return` expressions also have type `!`. For example we are allowed to
/// write:
///
/// ```
/// # #![feature(never_type)]
/// # fn foo() -> u32 {
/// let x: ! = {
/// return 123
/// };
/// # }
Copy link
Member

Choose a reason for hiding this comment

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

I feel like we should show the feature flag here, since it's needed to give x the proper type.

/// ```
///
/// Although the `let` is pointless here, it illustrates the meaning of `!`. Since `x` is never
/// assigned a value (because `return` returns from the entire function), `x` can be given type
/// `!`. We could also replace `return 123` with a `panic!` or a never-ending `loop` and this code
/// would still be valid.
///
/// A more realistic usage of `!` is in this code:
///
/// ```
/// # fn get_a_number() -> Option<u32> { None }
/// # loop {
/// let num: u32 = match get_a_number() {
/// Some(num) => num,
/// None => break,
/// };
/// # }
/// ```
///
/// 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
/// behaviour of the `!` type - expressions with type `!` will coerce into any other type.
///
/// [`u32`]: primitive.str.html
/// [`exit`]: process/fn.exit.html
///
/// # `!` and generics
///
/// The main place you'll see `!` used explicitly is in generic code. Consider the [`FromStr`]
/// trait:
///
/// ```
/// trait FromStr: Sized {
/// type Err;
/// fn from_str(s: &str) -> Result<Self, Self::Err>;
/// }
/// ```
///
/// When implementing this trait for [`String`] we need to pick a type for [`Err`]. And since
/// converting a string into a string will never result in an error, the appropriate type is `!`.
/// (Currently the type actually used is an enum with no variants, though this is only because `!`
/// was added to Rust at a later date and it may change in the future). With an [`Err`] type of
/// `!`, if we have to call [`String::from_str`] for some reason the result will be a
/// [`Result<String, !>`] which we can unpack like this:
///
/// ```ignore (string-from-str-error-type-is-not-never-yet)
/// let Ok(s) = String::from_str("hello");
Copy link
Member

Choose a reason for hiding this comment

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

I know these ignore blocks get a little info tooltip and callout border now, but i'd prefer it if we had a comment in here along the lines of "NOTE: This does not work today!", just to sate my paranoia about people blindly copy/pasting examples from docs.

/// ```
///
/// Since the [`Err`] variant contains a `!`, it can never occur. So we can exhaustively match on
/// [`Result<T, !>`] by just taking the [`Ok`] variant. This illustrates another behaviour of `!` -
/// it can be used to "delete" certain enum variants from generic types like `Result`.
///
/// [`String::from_str`]: str/trait.FromStr.html#tymethod.from_str
/// [`Result<String, !>`]: result/enum.Result.html
/// [`Result<T, !>`]: result/enum.Result.html
/// [`Ok`]: result/enum.Result.html#variant.Ok
/// [`String`]: string/struct.String.html
/// [`Err`]: result/enum.Result.html#variant.Err
/// [`FromStr`]: str/trait.FromStr.html
///
/// # `!` and traits
///
/// When writing your own traits, `!` should have an `impl` whenever there is an obvious `impl`
/// which doesn't `panic!`. As is turns out, most traits can have an `impl` for `!`. Take [`Debug`]
/// for example:
///
/// ```
/// # #![feature(never_type)]
/// # use std::fmt;
/// # trait Debug {
/// # fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result;
/// # }
/// impl Debug for ! {
/// fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
/// *self
/// }
/// }
/// ```
///
/// Once again we're using `!`'s ability to coerce into any other type, in this case
/// [`fmt::Result`]. Since this method takes a `&!` as an argument we know that it can never be
/// called (because there is no value of type `!` for it to be called with). Writing `*self`
/// essentially tells the compiler "We know that this code can never be run, so just treat the
/// entire function body has having type [`fmt::Result`]". This pattern can be used a lot when
/// implementing traits for `!`. Generally, any trait which only has methods which take a `self`
/// parameter should have such as impl.
///
/// On the other hand, one trait which would not be appropriate to implement is [`Default`]:
///
/// ```
/// trait Default {
/// fn default() -> Self;
/// }
/// ```
///
/// Since `!` has no values, it has no default value either. It's true that we could write an
/// `impl` for this which simply panics, but the same is true for any type (we could `impl
/// Default` for (eg.) [`File`] by just making [`default()`] panic.)
///
/// [`fmt::Result`]: fmt/type.Result.html
/// [`File`]: fs/struct.File.html
/// [`Debug`]: fmt/trait.Debug.html
/// [`Default`]: default/trait.Default.html
/// [`default()`]: default/trait.Default.html#tymethod.default
///
#[unstable(feature = "never_type_impls", issue = "35121")]
mod prim_never { }

#[doc(primitive = "char")]
//
/// A character type.
Expand Down