Skip to content

Commit

Permalink
Re-draft Deref docs
Browse files Browse the repository at this point in the history
Make general advice more explicit and note the difference between
generic and specific implementations.
  • Loading branch information
John-Mark Allen committed Jun 16, 2023
1 parent c14f67c commit 1b65edf
Showing 1 changed file with 75 additions and 45 deletions.
120 changes: 75 additions & 45 deletions library/core/src/ops/deref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,70 +3,100 @@
/// In addition to being used for explicit dereferencing operations with the
/// (unary) `*` operator in immutable contexts, `Deref` is also used implicitly
/// by the compiler in many circumstances. This mechanism is called
/// ['`Deref` coercion'][more]. In mutable contexts, [`DerefMut`] is used and
/// ['`Deref` coercion'][coercion]. In mutable contexts, [`DerefMut`] is used and
/// mutable deref coercion similarly occurs.
///
/// Deref coercion is a powerful language feature and has far-reaching
/// implications for every type implementing `Deref`: the compiler will often
/// implicitly call `Deref::deref`, as described [below][more]. For this reason,
/// `Deref` should not be implemented unless all of the implications of coercion
/// are understood and desired. Implementing `Deref` is most commonly desirable
/// when a type
///
/// 1. should transparently behave like a contained value; and
/// 2. does not have a rich set of methods in of itself, to avoid conflict or
/// confusion with methods on the `Target` type.
///
/// For example, [`Box<T>`][box] is perhaps the canonical example for `Deref`:
/// a `Box<T>` value **is** a `T` value that is heap-allocated, so deref
/// coercion intuitively makes `Box<T>` act like `T`. `Box<T>`
/// also has very few methods (though several associated functions), so it is
/// unlikely that users will find conflict with methods on their target type.
/// [`RefCell<T>`][refcell] also contains a value of `T`, but while it could
/// implement `DerefMut`, it does not since there is a rich API of methods to
/// explicitly manage the owned value: allowing deref coercion in this case
/// would hide the explicit management and likely cause confusion.
///
/// Types that implement `Deref` and `DerefMut` are often called "smart
/// **Warning:** Deref coercion is a powerful language feature which has
/// far-reaching implications for every type that implements `Deref`. The
/// compiler will silently insert calls to `Deref::deref`. For this reason, one
/// should be careful about implementing `Deref` and only do so when deref
/// coercion is desirable. See [below][implementing] for advice on when this is
/// typeically desired or undesirable.
///
/// Types that implement `Deref` or `DerefMut` are often called "smart
/// pointers" and the mechanism of deref coercion has been specifically designed
/// to facilitate the pointer-like behaviour that name suggests. Very often, the
/// only purpose of a "smart pointer" type is to change the ownership semantics
/// to facilitate the pointer-like behaviour that name suggests. Often, the
/// purpose of a "smart pointer" type is to change the ownership semantics
/// of a contained value (for example, [`Rc`][rc] or [`Cow`][cow]) or the
/// storage semantics of a contained value (for example, [`Box`][box]).
///
/// If deref coercion is not desirable, the [`AsRef`] or [`Borrow`][borrow]
/// traits are alternatives which should be preferred in most cases.
///
/// # Fallibility
///
/// **This trait's method should never fail**. Deref coercion means the compiler
/// will often implicitly call `Deref::deref`. Failure during
/// dereferencing can be extremely confusing when `Deref` is invoked implicitly.
///
/// # More on `Deref` coercion
/// # Deref coercion
///
/// If `T` implements `Deref<Target = U>`, and `x` is a value of type `T`, then:
/// If `T` implements `Deref<Target = U>`, and `v` is a value of type `T`, then:
///
/// * In immutable contexts, `*x` (where `T` is neither a reference nor a raw pointer)
/// is equivalent to `*Deref::deref(&x)`.
/// * In immutable contexts, `*v` (where `T` is neither a reference nor a raw
/// pointer) is equivalent to `*Deref::deref(&x)`.
/// * Values of type `&T` are coerced to values of type `&U`
/// * `T` implicitly implements all the methods of the type `U` which take the
/// `&self` receiver.
///
/// For more details, visit [the chapter in *The Rust Programming Language*][book]
/// as well as the reference sections on [the dereference operator][ref-deref-op],
/// [method resolution] and [type coercions].
/// [method resolution], and [type coercions].
///
/// # When to implement `Deref` or `DerefMut`
///
/// The same advice applies to both deref traits. In general, deref traits
/// **should** be implemented if:
///
/// 1. a value of the type transparently behaves like a value of the target
/// type;
/// 1. the implementation of the deref function is cheap; and
/// 1. users of the type will not be surprised by any deref coercion behaviour.
///
/// In general, deref traits **should not** be implemented if:
///
/// 1. the deref implementations could fail unexpectedly; or
/// 1. the type has methods that are likely to collide with methods on the
/// target type; or
/// 1. committing to deref coercion as part of the public API is not desirable.
///
/// Note that there's a large difference between implementing deref traits
/// generically over many target types, and doing so only for specific target
/// types.
///
/// Generic implementations, such as for [`Box<T>`][box] (which is generic over
/// every type) should be careful to few methods, since the target type
/// is unknown and therefore every method could collide with one on the target
/// type, causing confusion for users. `Box<T>` has few methods (though
/// several associated functions), partly for this reason.
///
/// Specific implementations, such as for [`String`][string] (which only
/// implements for `Target = str`) can have many methods, since avoiding
/// collision is much easier. `String` and `str` both have many methods, and
/// `String` additionally behaves as if it has every method of `str` because of
/// deref coercion.
///
/// Consider also that deref coericion means that deref traits are a much larger
/// part of a type's public API than any other trait as it is implicitly called
/// by the compiler. Therefore, it is advisable to consider whether this is
/// something you are comfortable supporting as a public API.
///
/// The [`AsRef`] and [`Borrow`][core::borrow::Borrow] traits have very similar
/// signatures to `Deref`. It may be desirable to implement either or both of
/// these, whether in addition to or rather than deref traits. See their
/// documentation for details.
///
/// # Fallibility
///
/// **This trait's method should never unexpectedly fail**. Deref coercion means
/// the compiler will often insert calls to `Deref::deref` implicitly. Failure
/// during dereferencing can be extremely confusing when `Deref` is invoked
/// implicitly. In the majority of uses it should be infallible, though it may
/// be acceptable to panic if the type is misused through programmer error, for
/// example.
///
/// [book]: ../../book/ch15-02-deref.html
/// [more]: #more-on-deref-coercion
/// [coercion]: #deref-coercion
/// [implementing]: #when-to-implement-deref-or-derefmut
/// [ref-deref-op]: ../../reference/expressions/operator-expr.html#the-dereference-operator
/// [method resolution]: ../../reference/expressions/method-call-expr.html
/// [type coercions]: ../../reference/type-coercions.html
/// [box]: ../../std/boxed/struct.Box.html
/// [refcell]: ../../std/cell/struct.RefCell.html
/// [rc]: ../../std/rc/struct.Rc.html
/// [cow]: ../../std/borrow/enum.Cow.html
/// [borrow]: ../../std/borrow/trait.Borrow.html
/// [box]: ../../alloc/boxed/struct.Box.html
/// [string]: ../../alloc/string/struct.String.html
/// [rc]: ../../alloc/rc/struct.Rc.html
/// [cow]: ../../alloc/borrow/enum.Cow.html
/// [borrow]: core::borrow::Borrow
///
/// # Examples
///
Expand Down

0 comments on commit 1b65edf

Please sign in to comment.