Rollup merge of #33815 - carols10cents:trait-documentation-clarifications, r=steveklabnik
Trait documentation clarifications
Hi! I've felt a bit of friction lately in figuring out how to write custom implementations of the `derive`able traits, so I decided to add to the docs :)
The docs for `Copy` are already excellent-- clear, useful sections that I only reordered a bit-- they're now:
* General explanation
* When can my type be `Copy`?
* When can my type _not_ be `Copy`?
* When should my type be `Copy`?
* Derivable
* How can I implement `Copy`?
I didn't add all these sections for all the traits, but I did make sure all the derivable traits had a consistent "Derivable" section that explained what the derived implementation does and a "How can I implement" section that has an example.
Please check me for correctness-- I tried to do research to make sure I was saying accurate things but I'm still learning! ❤️ I'd also love suggestions on information to add that is still missing-- I think these traits are important and deserve to have awesome docs!
This commit is contained in:
commit
967c3880d8
@ -46,14 +46,42 @@
|
||||
|
||||
use marker::Sized;
|
||||
|
||||
/// A common trait for cloning an object.
|
||||
/// A common trait for the ability to explicitly duplicate an object.
|
||||
///
|
||||
/// This trait can be used with `#[derive]`.
|
||||
/// Differs from `Copy` in that `Copy` is implicit and extremely inexpensive, while
|
||||
/// `Clone` is always explicit and may or may not be expensive. In order to enforce
|
||||
/// these characteristics, Rust does not allow you to reimplement `Copy`, but you
|
||||
/// may reimplement `Clone` and run arbitrary code.
|
||||
///
|
||||
/// Since `Clone` is more general than `Copy`, you can automatically make anything
|
||||
/// `Copy` be `Clone` as well.
|
||||
///
|
||||
/// ## Derivable
|
||||
///
|
||||
/// This trait can be used with `#[derive]` if all fields are `Clone`. The `derive`d
|
||||
/// implementation of `clone()` calls `clone()` on each field.
|
||||
///
|
||||
/// ## How can I implement `Clone`?
|
||||
///
|
||||
/// Types that are `Copy` should have a trivial implementation of `Clone`. More formally:
|
||||
/// if `T: Copy`, `x: T`, and `y: &T`, then `let x = y.clone();` is equivalent to `let x = *y;`.
|
||||
/// Manual implementations should be careful to uphold this invariant; however, unsafe code
|
||||
/// must not rely on it to ensure memory safety.
|
||||
///
|
||||
/// An example is an array holding more than 32 elements of a type that is `Clone`; the standard
|
||||
/// library only implements `Clone` up until arrays of size 32. In this case, the implementation of
|
||||
/// `Clone` cannot be `derive`d, but can be implemented as:
|
||||
///
|
||||
/// ```
|
||||
/// #[derive(Copy)]
|
||||
/// struct Stats {
|
||||
/// frequencies: [i32; 100],
|
||||
/// }
|
||||
///
|
||||
/// impl Clone for Stats {
|
||||
/// fn clone(&self) -> Stats { *self }
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub trait Clone : Sized {
|
||||
/// Returns a copy of the value.
|
||||
|
@ -53,12 +53,43 @@
|
||||
/// symmetrically and transitively: if `T: PartialEq<U>` and `U: PartialEq<V>`
|
||||
/// then `U: PartialEq<T>` and `T: PartialEq<V>`.
|
||||
///
|
||||
/// ## Derivable
|
||||
///
|
||||
/// This trait can be used with `#[derive]`. When `derive`d on structs, two
|
||||
/// instances are equal if all fields are equal, and not equal if any fields
|
||||
/// are not equal. When `derive`d on enums, each variant is equal to itself
|
||||
/// and not equal to the other variants.
|
||||
///
|
||||
/// ## How can I implement `PartialEq`?
|
||||
///
|
||||
/// PartialEq only requires the `eq` method to be implemented; `ne` is defined
|
||||
/// in terms of it by default. Any manual implementation of `ne` *must* respect
|
||||
/// the rule that `eq` is a strict inverse of `ne`; that is, `!(a == b)` if and
|
||||
/// only if `a != b`.
|
||||
///
|
||||
/// This trait can be used with `#[derive]`.
|
||||
/// An example implementation for a domain in which two books are considered
|
||||
/// the same book if their ISBN matches, even if the formats differ:
|
||||
///
|
||||
/// ```
|
||||
/// enum BookFormat { Paperback, Hardback, Ebook }
|
||||
/// struct Book {
|
||||
/// isbn: i32,
|
||||
/// format: BookFormat,
|
||||
/// }
|
||||
///
|
||||
/// impl PartialEq for Book {
|
||||
/// fn eq(&self, other: &Book) -> bool {
|
||||
/// self.isbn == other.isbn
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// let b1 = Book { isbn: 3, format: BookFormat::Paperback };
|
||||
/// let b2 = Book { isbn: 3, format: BookFormat::Ebook };
|
||||
/// let b3 = Book { isbn: 10, format: BookFormat::Paperback };
|
||||
///
|
||||
/// assert!(b1 == b2);
|
||||
/// assert!(b1 != b3);
|
||||
/// ```
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@ -96,7 +127,32 @@ fn ne(&self, other: &Rhs) -> bool { !self.eq(other) }
|
||||
/// This property cannot be checked by the compiler, and therefore `Eq` implies
|
||||
/// `PartialEq`, and has no extra methods.
|
||||
///
|
||||
/// This trait can be used with `#[derive]`.
|
||||
/// ## Derivable
|
||||
///
|
||||
/// This trait can be used with `#[derive]`. When `derive`d, because `Eq` has
|
||||
/// no extra methods, it is only informing the compiler that this is an
|
||||
/// equivalence relation rather than a partial equivalence relation. Note that
|
||||
/// the `derive` strategy requires all fields are `PartialEq`, which isn't
|
||||
/// always desired.
|
||||
///
|
||||
/// ## How can I implement `Eq`?
|
||||
///
|
||||
/// If you cannot use the `derive` strategy, specify that your type implements
|
||||
/// `Eq`, which has no methods:
|
||||
///
|
||||
/// ```
|
||||
/// enum BookFormat { Paperback, Hardback, Ebook }
|
||||
/// struct Book {
|
||||
/// isbn: i32,
|
||||
/// format: BookFormat,
|
||||
/// }
|
||||
/// impl PartialEq for Book {
|
||||
/// fn eq(&self, other: &Book) -> bool {
|
||||
/// self.isbn == other.isbn
|
||||
/// }
|
||||
/// }
|
||||
/// impl Eq for Book {}
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub trait Eq: PartialEq<Self> {
|
||||
// FIXME #13101: this method is used solely by #[deriving] to
|
||||
@ -190,8 +246,49 @@ pub fn reverse(self) -> Ordering {
|
||||
/// - total and antisymmetric: exactly one of `a < b`, `a == b` or `a > b` is true; and
|
||||
/// - transitive, `a < b` and `b < c` implies `a < c`. The same must hold for both `==` and `>`.
|
||||
///
|
||||
/// ## Derivable
|
||||
///
|
||||
/// This trait can be used with `#[derive]`. When `derive`d, it will produce a lexicographic
|
||||
/// ordering based on the top-to-bottom declaration order of the struct's members.
|
||||
///
|
||||
/// ## How can I implement `Ord`?
|
||||
///
|
||||
/// `Ord` requires that the type also be `PartialOrd` and `Eq` (which requires `PartialEq`).
|
||||
///
|
||||
/// Then you must define an implementation for `cmp()`. You may find it useful to use
|
||||
/// `cmp()` on your type's fields.
|
||||
///
|
||||
/// Here's an example where you want to sort people by height only, disregarding `id`
|
||||
/// and `name`:
|
||||
///
|
||||
/// ```
|
||||
/// use std::cmp::Ordering;
|
||||
///
|
||||
/// #[derive(Eq)]
|
||||
/// struct Person {
|
||||
/// id: u32,
|
||||
/// name: String,
|
||||
/// height: u32,
|
||||
/// }
|
||||
///
|
||||
/// impl Ord for Person {
|
||||
/// fn cmp(&self, other: &Person) -> Ordering {
|
||||
/// self.height.cmp(&other.height)
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl PartialOrd for Person {
|
||||
/// fn partial_cmp(&self, other: &Person) -> Option<Ordering> {
|
||||
/// Some(self.cmp(other))
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl PartialEq for Person {
|
||||
/// fn eq(&self, other: &Person) -> bool {
|
||||
/// self.height == other.height
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub trait Ord: Eq + PartialOrd<Self> {
|
||||
/// This method returns an `Ordering` between `self` and `other`.
|
||||
@ -242,6 +339,13 @@ fn partial_cmp(&self, other: &Ordering) -> Option<Ordering> {
|
||||
/// transitively: if `T: PartialOrd<U>` and `U: PartialOrd<V>` then `U: PartialOrd<T>` and `T:
|
||||
/// PartialOrd<V>`.
|
||||
///
|
||||
/// ## Derivable
|
||||
///
|
||||
/// This trait can be used with `#[derive]`. When `derive`d, it will produce a lexicographic
|
||||
/// ordering based on the top-to-bottom declaration order of the struct's members.
|
||||
///
|
||||
/// ## How can I implement `Ord`?
|
||||
///
|
||||
/// PartialOrd only requires implementation of the `partial_cmp` method, with the others generated
|
||||
/// from default implementations.
|
||||
///
|
||||
@ -249,8 +353,64 @@ fn partial_cmp(&self, other: &Ordering) -> Option<Ordering> {
|
||||
/// total order. For example, for floating point numbers, `NaN < 0 == false` and `NaN >= 0 ==
|
||||
/// false` (cf. IEEE 754-2008 section 5.11).
|
||||
///
|
||||
/// This trait can be used with `#[derive]`. When `derive`d, it will produce an ordering
|
||||
/// based on the top-to-bottom declaration order of the struct's members.
|
||||
/// `PartialOrd` requires your type to be `PartialEq`.
|
||||
///
|
||||
/// If your type is `Ord`, you can implement `partial_cmp()` by using `cmp()`:
|
||||
///
|
||||
/// ```
|
||||
/// use std::cmp::Ordering;
|
||||
///
|
||||
/// #[derive(Eq)]
|
||||
/// struct Person {
|
||||
/// id: u32,
|
||||
/// name: String,
|
||||
/// height: u32,
|
||||
/// }
|
||||
///
|
||||
/// impl PartialOrd for Person {
|
||||
/// fn partial_cmp(&self, other: &Person) -> Option<Ordering> {
|
||||
/// Some(self.cmp(other))
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl Ord for Person {
|
||||
/// fn cmp(&self, other: &Person) -> Ordering {
|
||||
/// self.height.cmp(&other.height)
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl PartialEq for Person {
|
||||
/// fn eq(&self, other: &Person) -> bool {
|
||||
/// self.height == other.height
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// You may also find it useful to use `partial_cmp()` on your type`s fields. Here
|
||||
/// is an example of `Person` types who have a floating-point `height` field that
|
||||
/// is the only field to be used for sorting:
|
||||
///
|
||||
/// ```
|
||||
/// use std::cmp::Ordering;
|
||||
///
|
||||
/// struct Person {
|
||||
/// id: u32,
|
||||
/// name: String,
|
||||
/// height: f64,
|
||||
/// }
|
||||
///
|
||||
/// impl PartialOrd for Person {
|
||||
/// fn partial_cmp(&self, other: &Person) -> Option<Ordering> {
|
||||
/// self.height.partial_cmp(&other.height)
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl PartialEq for Person {
|
||||
/// fn eq(&self, other: &Person) -> bool {
|
||||
/// self.height == other.height
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
@ -9,76 +9,6 @@
|
||||
// except according to those terms.
|
||||
|
||||
//! The `Default` trait for types which may have meaningful default values.
|
||||
//!
|
||||
//! Sometimes, you want to fall back to some kind of default value, and
|
||||
//! don't particularly care what it is. This comes up often with `struct`s
|
||||
//! that define a set of options:
|
||||
//!
|
||||
//! ```
|
||||
//! # #[allow(dead_code)]
|
||||
//! struct SomeOptions {
|
||||
//! foo: i32,
|
||||
//! bar: f32,
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! How can we define some default values? You can use `Default`:
|
||||
//!
|
||||
//! ```
|
||||
//! # #[allow(dead_code)]
|
||||
//! #[derive(Default)]
|
||||
//! struct SomeOptions {
|
||||
//! foo: i32,
|
||||
//! bar: f32,
|
||||
//! }
|
||||
//!
|
||||
//!
|
||||
//! fn main() {
|
||||
//! let options: SomeOptions = Default::default();
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! Now, you get all of the default values. Rust implements `Default` for various primitives types.
|
||||
//! If you have your own type, you need to implement `Default` yourself:
|
||||
//!
|
||||
//! ```
|
||||
//! # #![allow(dead_code)]
|
||||
//! enum Kind {
|
||||
//! A,
|
||||
//! B,
|
||||
//! C,
|
||||
//! }
|
||||
//!
|
||||
//! impl Default for Kind {
|
||||
//! fn default() -> Kind { Kind::A }
|
||||
//! }
|
||||
//!
|
||||
//! #[derive(Default)]
|
||||
//! struct SomeOptions {
|
||||
//! foo: i32,
|
||||
//! bar: f32,
|
||||
//! baz: Kind,
|
||||
//! }
|
||||
//!
|
||||
//!
|
||||
//! fn main() {
|
||||
//! let options: SomeOptions = Default::default();
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! If you want to override a particular option, but still retain the other defaults:
|
||||
//!
|
||||
//! ```
|
||||
//! # #[allow(dead_code)]
|
||||
//! # #[derive(Default)]
|
||||
//! # struct SomeOptions {
|
||||
//! # foo: i32,
|
||||
//! # bar: f32,
|
||||
//! # }
|
||||
//! fn main() {
|
||||
//! let options = SomeOptions { foo: 42, ..Default::default() };
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
#![stable(feature = "rust1", since = "1.0.0")]
|
||||
|
||||
@ -86,8 +16,72 @@
|
||||
|
||||
/// A trait for giving a type a useful default value.
|
||||
///
|
||||
/// A struct can derive default implementations of `Default` for basic types using
|
||||
/// `#[derive(Default)]`.
|
||||
/// Sometimes, you want to fall back to some kind of default value, and
|
||||
/// don't particularly care what it is. This comes up often with `struct`s
|
||||
/// that define a set of options:
|
||||
///
|
||||
/// ```
|
||||
/// # #[allow(dead_code)]
|
||||
/// struct SomeOptions {
|
||||
/// foo: i32,
|
||||
/// bar: f32,
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// How can we define some default values? You can use `Default`:
|
||||
///
|
||||
/// ```
|
||||
/// # #[allow(dead_code)]
|
||||
/// #[derive(Default)]
|
||||
/// struct SomeOptions {
|
||||
/// foo: i32,
|
||||
/// bar: f32,
|
||||
/// }
|
||||
///
|
||||
///
|
||||
/// fn main() {
|
||||
/// let options: SomeOptions = Default::default();
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Now, you get all of the default values. Rust implements `Default` for various primitives types.
|
||||
///
|
||||
/// If you want to override a particular option, but still retain the other defaults:
|
||||
///
|
||||
/// ```
|
||||
/// # #[allow(dead_code)]
|
||||
/// # #[derive(Default)]
|
||||
/// # struct SomeOptions {
|
||||
/// # foo: i32,
|
||||
/// # bar: f32,
|
||||
/// # }
|
||||
/// fn main() {
|
||||
/// let options = SomeOptions { foo: 42, ..Default::default() };
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ## Derivable
|
||||
///
|
||||
/// This trait can be used with `#[derive]` if all of the type's fields implement
|
||||
/// `Default`. When `derive`d, it will use the default value for each field's type.
|
||||
///
|
||||
/// ## How can I implement `Default`?
|
||||
///
|
||||
/// Provide an implementation for the `default()` method that returns the value of
|
||||
/// your type that should be the default:
|
||||
///
|
||||
/// ```
|
||||
/// # #![allow(dead_code)]
|
||||
/// enum Kind {
|
||||
/// A,
|
||||
/// B,
|
||||
/// C,
|
||||
/// }
|
||||
///
|
||||
/// impl Default for Kind {
|
||||
/// fn default() -> Kind { Kind::A }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
@ -318,7 +318,11 @@ fn fmt(&self, fmt: &mut Formatter) -> Result {
|
||||
///
|
||||
/// [module]: ../../std/fmt/index.html
|
||||
///
|
||||
/// This trait can be used with `#[derive]`.
|
||||
/// This trait can be used with `#[derive]` if all fields implement `Debug`. When
|
||||
/// `derive`d for structs, it will use the name of the `struct`, then `{`, then a
|
||||
/// comma-separated list of each field's name and `Debug` value, then `}`. For
|
||||
/// `enum`s, it will use the name of the variant and, if applicable, `(`, then the
|
||||
/// `Debug` values of the fields, then `)`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
@ -38,7 +38,7 @@
|
||||
//! ```
|
||||
//!
|
||||
//! If you need more control over how a value is hashed, you need to implement
|
||||
//! the trait `Hash`:
|
||||
//! the `Hash` trait:
|
||||
//!
|
||||
//! ```rust
|
||||
//! use std::hash::{Hash, Hasher, SipHasher};
|
||||
@ -97,7 +97,33 @@
|
||||
/// In other words, if two keys are equal, their hashes should also be equal.
|
||||
/// `HashMap` and `HashSet` both rely on this behavior.
|
||||
///
|
||||
/// This trait can be used with `#[derive]`.
|
||||
/// ## Derivable
|
||||
///
|
||||
/// This trait can be used with `#[derive]` if all fields implement `Hash`.
|
||||
/// When `derive`d, the resulting hash will be the combination of the values
|
||||
/// from calling `.hash()` on each field.
|
||||
///
|
||||
/// ## How can I implement `Hash`?
|
||||
///
|
||||
/// If you need more control over how a value is hashed, you need to implement
|
||||
/// the `Hash` trait:
|
||||
///
|
||||
/// ```
|
||||
/// use std::hash::{Hash, Hasher};
|
||||
///
|
||||
/// struct Person {
|
||||
/// id: u32,
|
||||
/// name: String,
|
||||
/// phone: u64,
|
||||
/// }
|
||||
///
|
||||
/// impl Hash for Person {
|
||||
/// fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
/// self.id.hash(state);
|
||||
/// self.phone.hash(state);
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub trait Hash {
|
||||
/// Feeds this value into the state given, updating the hasher as necessary.
|
||||
|
@ -136,6 +136,26 @@ pub trait Unsize<T: ?Sized> {
|
||||
/// the trait `Copy` may not be implemented for this type; field `points` does not implement `Copy`
|
||||
/// ```
|
||||
///
|
||||
/// ## When can my type _not_ be `Copy`?
|
||||
///
|
||||
/// Some types can't be copied safely. For example, copying `&mut T` would create an aliased
|
||||
/// mutable reference, and copying `String` would result in two attempts to free the same buffer.
|
||||
///
|
||||
/// Generalizing the latter case, any type implementing `Drop` can't be `Copy`, because it's
|
||||
/// managing some resource besides its own `size_of::<T>()` bytes.
|
||||
///
|
||||
/// ## When should my type be `Copy`?
|
||||
///
|
||||
/// Generally speaking, if your type _can_ implement `Copy`, it should. There's one important thing
|
||||
/// to consider though: if you think your type may _not_ be able to implement `Copy` in the future,
|
||||
/// then it might be prudent to not implement `Copy`. This is because removing `Copy` is a breaking
|
||||
/// change: that second example would fail to compile if we made `Foo` non-`Copy`.
|
||||
///
|
||||
/// ## Derivable
|
||||
///
|
||||
/// This trait can be used with `#[derive]` if all of its components implement `Copy` and the type
|
||||
/// implements `Clone`. The implementation will copy the bytes of each field using `memcpy`.
|
||||
///
|
||||
/// ## How can I implement `Copy`?
|
||||
///
|
||||
/// There are two ways to implement `Copy` on your type:
|
||||
@ -155,25 +175,6 @@ pub trait Unsize<T: ?Sized> {
|
||||
///
|
||||
/// There is a small difference between the two: the `derive` strategy will also place a `Copy`
|
||||
/// bound on type parameters, which isn't always desired.
|
||||
///
|
||||
/// ## When can my type _not_ be `Copy`?
|
||||
///
|
||||
/// Some types can't be copied safely. For example, copying `&mut T` would create an aliased
|
||||
/// mutable reference, and copying `String` would result in two attempts to free the same buffer.
|
||||
///
|
||||
/// Generalizing the latter case, any type implementing `Drop` can't be `Copy`, because it's
|
||||
/// managing some resource besides its own `size_of::<T>()` bytes.
|
||||
///
|
||||
/// ## When should my type be `Copy`?
|
||||
///
|
||||
/// Generally speaking, if your type _can_ implement `Copy`, it should. There's one important thing
|
||||
/// to consider though: if you think your type may _not_ be able to implement `Copy` in the future,
|
||||
/// then it might be prudent to not implement `Copy`. This is because removing `Copy` is a breaking
|
||||
/// change: that second example would fail to compile if we made `Foo` non-`Copy`.
|
||||
///
|
||||
/// # Derivable
|
||||
///
|
||||
/// This trait can be used with `#[derive]`.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[lang = "copy"]
|
||||
pub trait Copy : Clone {
|
||||
|
Loading…
Reference in New Issue
Block a user