diff --git a/library/core/src/ops/deref.rs b/library/core/src/ops/deref.rs index c07661d14efb..a2b773b82f9a 100644 --- a/library/core/src/ops/deref.rs +++ b/library/core/src/ops/deref.rs @@ -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`][box] is perhaps the canonical example for `Deref`: -/// a `Box` value **is** a `T` value that is heap-allocated, so deref -/// coercion intuitively makes `Box` act like `T`. `Box` -/// 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`][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`, and `x` is a value of type `T`, then: +/// If `T` implements `Deref`, 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`][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` 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 ///