about summary refs log tree commit diff
diff options
context:
space:
mode:
authorjmaargh <jmaargh@gmail.com>2023-04-14 23:49:57 +0100
committerjmaargh <jmaargh@gmail.com>2023-11-04 17:47:25 +0000
commit7a2f83fa3f78181af159c03c60a974ac3554dd05 (patch)
treeee498807b30564ebe95a61faa078188387879c38
parent0d1664674a620f2c139be756a5cf7b1b057bc3a9 (diff)
downloadrust-7a2f83fa3f78181af159c03c60a974ac3554dd05.tar.gz
rust-7a2f83fa3f78181af159c03c60a974ac3554dd05.zip
Draft fleshed-out deref docs
Re-draft Deref docs

Make general advice more explicit and note the difference between
generic and specific implementations.

Re-draft DerefMut docs in-line with Deref

Fix Deref docs typos

Fix broken links

Clarify advice for specific-over-generic impls

Add comment addressing Issue #73682

x fmt

Copy faillibility warning to DerefMut
-rw-r--r--library/core/src/convert/mod.rs4
-rw-r--r--library/core/src/ops/deref.rs162
2 files changed, 125 insertions, 41 deletions
diff --git a/library/core/src/convert/mod.rs b/library/core/src/convert/mod.rs
index 89125b7955e..8c01b0973d6 100644
--- a/library/core/src/convert/mod.rs
+++ b/library/core/src/convert/mod.rs
@@ -138,7 +138,7 @@ pub const fn identity<T>(x: T) -> T {
 ///
 /// [dereferenceable types]: core::ops::Deref
 /// [pointed-to value]: core::ops::Deref::Target
-/// ['`Deref` coercion']: core::ops::Deref#more-on-deref-coercion
+/// ['`Deref` coercion']: core::ops::Deref#deref-coercion
 ///
 /// ```
 /// let x = Box::new(5i32);
@@ -244,7 +244,7 @@ pub trait AsRef<T: ?Sized> {
 ///
 /// [mutably dereferenceable types]: core::ops::DerefMut
 /// [pointed-to value]: core::ops::Deref::Target
-/// ['`Deref` coercion']: core::ops::DerefMut#more-on-deref-coercion
+/// ['`Deref` coercion']: core::ops::DerefMut#mutable-deref-coercion
 ///
 /// ```
 /// let mut x = Box::new(5i32);
diff --git a/library/core/src/ops/deref.rs b/library/core/src/ops/deref.rs
index d6f2f5ca366..99adbb91599 100644
--- a/library/core/src/ops/deref.rs
+++ b/library/core/src/ops/deref.rs
@@ -3,40 +3,107 @@
 /// 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.
+/// ["`Deref` coercion"][coercion]. In mutable contexts, [`DerefMut`] is used and
+/// mutable deref coercion similarly occurs.
 ///
-/// Implementing `Deref` for smart pointers makes accessing the data behind them
-/// convenient, which is why they implement `Deref`. On the other hand, the
-/// rules regarding `Deref` and [`DerefMut`] were designed specifically to
-/// accommodate smart pointers. Because of this, **`Deref` should only be
-/// implemented for smart pointers** to avoid confusion.
+/// **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
+/// typically desirable or undesirable.
 ///
-/// For similar reasons, **this trait should never fail**. Failure during
-/// dereferencing can be extremely confusing when `Deref` is invoked implicitly.
+/// 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. 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]).
 ///
-/// Violating these requirements is a logic error. The behavior resulting from a logic error is not
-/// specified, but users of the trait must ensure that such logic errors do *not* result in
-/// undefined behavior. This means that `unsafe` code **must not** rely on the correctness of this
-/// method.
+/// # Deref coercion
 ///
-/// # More on `Deref` coercion
+/// If `T` implements `Deref<Target = U>`, and `v` is a value of type `T`, then:
 ///
-/// If `T` implements `Deref<Target = U>`, and `x` 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(&v)`.
 /// * Values of type `&T` are coerced to values of type `&U`
-/// * `T` implicitly implements all the (immutable) methods of the 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 and dereferences to `T`) should be careful to provide few or no
+/// methods, since the target type is unknown and therefore every method could
+/// collide with one on the target type, causing confusion for users.
+/// `impl<T> Box<T>` has no methods (though several associated functions),
+/// partly for this reason.
+///
+/// Specific implementations, such as for [`String`][string] (whose `Deref`
+/// implementation has `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. The implementing type may also be generic while the
+/// implementation is still specific in this sense; for example, [`Vec<T>`][vec]
+/// dereferences to `[T]`, so methods of `T` are not applicable.
+///
+/// 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.
+///
+/// However, infallibility is not enforced and therefore not guaranteed.
+/// As such, `unsafe` code should not rely on infallibility in general for
+/// soundness.
 ///
 /// [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]: ../../alloc/boxed/struct.Box.html
+/// [string]: ../../alloc/string/struct.String.html
+/// [vec]: ../../alloc/vec/struct.Vec.html
+/// [rc]: ../../alloc/rc/struct.Rc.html
+/// [cow]: ../../alloc/borrow/enum.Cow.html
 ///
 /// # Examples
 ///
@@ -107,30 +174,29 @@ impl<T: ?Sized> Deref for &mut T {
 /// In addition to being used for explicit dereferencing operations with the
 /// (unary) `*` operator in mutable contexts, `DerefMut` is also used implicitly
 /// by the compiler in many circumstances. This mechanism is called
-/// ['`Deref` coercion'][more]. In immutable contexts, [`Deref`] is used.
-///
-/// Implementing `DerefMut` for smart pointers makes mutating the data behind
-/// them convenient, which is why they implement `DerefMut`. On the other hand,
-/// the rules regarding [`Deref`] and `DerefMut` were designed specifically to
-/// accommodate smart pointers. Because of this, **`DerefMut` should only be
-/// implemented for smart pointers** to avoid confusion.
+/// ["mutable deref coercion"][coercion]. In immutable contexts, [`Deref`] is used.
 ///
-/// For similar reasons, **this trait should never fail**. Failure during
-/// dereferencing can be extremely confusing when `DerefMut` is invoked
-/// implicitly.
+/// **Warning:** Deref coercion is a powerful language feature which has
+/// far-reaching implications for every type that implements `DerefMut`. The
+/// compiler will silently insert calls to `DerefMut::deref_mut`. For this
+/// reason, one should be careful about implementing `DerefMut` and only do so
+/// when mutable deref coercion is desirable. See [the `Deref` docs][implementing]
+/// for advice on when this is typically desirable or undesirable.
 ///
-/// Violating these requirements is a logic error. The behavior resulting from a logic error is not
-/// specified, but users of the trait must ensure that such logic errors do *not* result in
-/// undefined behavior. This means that `unsafe` code **must not** rely on the correctness of this
-/// method.
+/// Types that implement `DerefMut` or `Deref` are often called "smart
+/// pointers" and the mechanism of deref coercion has been specifically designed
+/// 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]).
 ///
-/// # More on `Deref` coercion
+/// # Mutable deref coercion
 ///
-/// If `T` implements `DerefMut<Target = U>`, and `x` is a value of type `T`,
+/// If `T` implements `DerefMut<Target = U>`, and `v` is a value of type `T`,
 /// then:
 ///
-/// * In mutable contexts, `*x` (where `T` is neither a reference nor a raw pointer)
-///   is equivalent to `*DerefMut::deref_mut(&mut x)`.
+/// * In mutable contexts, `*v` (where `T` is neither a reference nor a raw pointer)
+///   is equivalent to `*DerefMut::deref_mut(&mut v)`.
 /// * Values of type `&mut T` are coerced to values of type `&mut U`
 /// * `T` implicitly implements all the (mutable) methods of the type `U`.
 ///
@@ -138,11 +204,29 @@ impl<T: ?Sized> Deref for &mut T {
 /// as well as the reference sections on [the dereference operator][ref-deref-op],
 /// [method resolution] and [type coercions].
 ///
+/// # Fallibility
+///
+/// **This trait's method should never unexpectedly fail**. Deref coercion means
+/// the compiler will often insert calls to `DerefMut::deref_mut` implicitly.
+/// Failure during dereferencing can be extremely confusing when `DerefMut` 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.
+///
+/// However, infallibility is not enforced and therefore not guaranteed.
+/// As such, `unsafe` code should not rely on infallibility in general for
+/// soundness.
+///
 /// [book]: ../../book/ch15-02-deref.html
-/// [more]: #more-on-deref-coercion
+/// [coercion]: #mutable-deref-coercion
+/// [implementing]: Deref#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]: ../../alloc/boxed/struct.Box.html
+/// [string]: ../../alloc/string/struct.String.html
+/// [rc]: ../../alloc/rc/struct.Rc.html
+/// [cow]: ../../alloc/borrow/enum.Cow.html
 ///
 /// # Examples
 ///