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

Deref docs: expand and remove "smart pointer" qualifier #110340

Merged
merged 1 commit into from
Nov 4, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 2 additions & 2 deletions library/core/src/convert/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
162 changes: 123 additions & 39 deletions library/core/src/ops/deref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Comment on lines +92 to +94
Copy link
Contributor Author

Choose a reason for hiding this comment

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

There was a merge conflict from the change introduced in PR #115607, which added the following (after the previous comment about how this "...must not fail..."):

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.

Frankly, this took me a while to understand at all (what are "these requirements", exactly? by "users" do we mean implementors of this trait, or some other users? this trait is safe so we can't cause undefined behaviour without unsafe code so... what are we talking about?) I guess this isn't helped by the fact that paragraph was pasted near-verbatim across several structs, rather than written for each context.

Reviewing the original issue that PR was solving ( #73682 ), I decided to replace it with the above, which I think covers the required ground while being more understandable.

///
/// [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
///
Expand Down Expand Up @@ -107,42 +174,59 @@ 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`.
///
/// 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].
///
/// # 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
///
Expand Down
Loading