docs: Improve AsRef / AsMut docs on blanket impls
- Explicitly mention that `AsRef` and `AsMut` do not auto-dereference generally for all dereferencable types (but only if inner type is a shared and/or mutable reference) - Give advice to not use `AsRef` or `AsMut` for the sole purpose of dereferencing - Suggest providing a transitive `AsRef` or `AsMut` implementation for types which implement `Deref` - Add new section "Reflexivity" in documentation comments for `AsRef` and `AsMut` - Provide better example for `AsMut` - Added heading "Relation to `Borrow`" in `AsRef`'s docs to improve structure Issue #45742 and a corresponding FIXME in the libcore suggest that `AsRef` and `AsMut` should provide a blanket implementation over `Deref`. As that is difficult to realize at the moment, this commit updates the documentation to better describe the status-quo and to give advice on how to use `AsRef` and `AsMut`.
This commit is contained in:
parent
c3f35504d1
commit
551d921de0
@ -25,6 +25,7 @@
|
||||
//! # Generic Implementations
|
||||
//!
|
||||
//! - [`AsRef`] and [`AsMut`] auto-dereference if the inner type is a reference
|
||||
//! (but not generally for all [dereferenceable types][core::ops::Deref])
|
||||
//! - [`From`]`<U> for T` implies [`Into`]`<T> for U`
|
||||
//! - [`TryFrom`]`<U> for T` implies [`TryInto`]`<T> for U`
|
||||
//! - [`From`] and [`Into`] are reflexive, which means that all types can
|
||||
@ -108,10 +109,12 @@ pub const fn identity<T>(x: T) -> T {
|
||||
/// If you need to do a costly conversion it is better to implement [`From`] with type
|
||||
/// `&T` or write a custom function.
|
||||
///
|
||||
/// # Relation to `Borrow`
|
||||
///
|
||||
/// `AsRef` has the same signature as [`Borrow`], but [`Borrow`] is different in a few aspects:
|
||||
///
|
||||
/// - Unlike `AsRef`, [`Borrow`] has a blanket impl for any `T`, and can be used to accept either
|
||||
/// a reference or a value.
|
||||
/// a reference or a value. (See also note on `AsRef`'s reflexibility below.)
|
||||
/// - [`Borrow`] also requires that [`Hash`], [`Eq`] and [`Ord`] for a borrowed value are
|
||||
/// equivalent to those of the owned value. For this reason, if you want to
|
||||
/// borrow only a single field of a struct you can implement `AsRef`, but not [`Borrow`].
|
||||
@ -121,9 +124,55 @@ pub const fn identity<T>(x: T) -> T {
|
||||
///
|
||||
/// # Generic Implementations
|
||||
///
|
||||
/// - `AsRef` auto-dereferences if the inner type is a reference or a mutable
|
||||
/// reference (e.g.: `foo.as_ref()` will work the same if `foo` has type
|
||||
/// `&mut Foo` or `&&mut Foo`)
|
||||
/// `AsRef` auto-dereferences if the inner type is a reference or a mutable reference
|
||||
/// (e.g.: `foo.as_ref()` will work the same if `foo` has type `&mut Foo` or `&&mut Foo`).
|
||||
///
|
||||
/// Note that due to historic reasons, the above currently does not hold generally for all
|
||||
/// [dereferenceable types], e.g. `foo.as_ref()` will *not* work the same as
|
||||
/// `Box::new(foo).as_ref()`. Instead, many smart pointers provide an `as_ref` implementation which
|
||||
/// simply returns a reference to the [pointed-to value] (but do not perform a cheap
|
||||
/// reference-to-reference conversion for that value). However, [`AsRef::as_ref`] should not be
|
||||
/// used for the sole purpose of dereferencing; instead ['`Deref` coercion'] can be used:
|
||||
///
|
||||
/// [dereferenceable types]: core::ops::Deref
|
||||
/// [pointed-to value]: core::ops::Deref::Target
|
||||
/// ['`Deref` coercion']: core::ops::Deref#more-on-deref-coercion
|
||||
///
|
||||
/// ```
|
||||
/// let x = Box::new(5i32);
|
||||
/// // Avoid this:
|
||||
/// // let y: &i32 = x.as_ref();
|
||||
/// // Better just write:
|
||||
/// let y: &i32 = &x;
|
||||
/// ```
|
||||
///
|
||||
/// Types which implement [`Deref`][core::ops::Deref] should consider implementing `AsRef` as
|
||||
/// follows:
|
||||
///
|
||||
/// ```
|
||||
/// impl<T> AsRef<T> for SomeType
|
||||
/// where
|
||||
/// T: ?Sized,
|
||||
/// <SomeType as Deref>::Target: AsRef<T>,
|
||||
/// {
|
||||
/// fn as_ref(&self) -> &T {
|
||||
/// self.deref().as_ref()
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// # Reflexivity
|
||||
///
|
||||
/// Ideally, `AsRef` would be reflexive, that is there is an `impl<T: ?Sized> AsRef<T> for T`, with
|
||||
/// [`as_ref`][AsRef::as_ref] simply returning its argument unchanged.
|
||||
/// Such a blanket implementation is currently *not* provided due to technical restrictions of
|
||||
/// Rust's type system (it would be overlapping with another existing blanket implementation for
|
||||
/// `&T where T: AsRef<U>` which allows `AsRef` to auto-dereference, see "Generic Implementations"
|
||||
/// above).
|
||||
///
|
||||
/// A trivial implementation of `AsRef<T> for T` must be added explicitly for a particular type `T`
|
||||
/// where needed or desired. Note, however, that not all types from `std` contain such an
|
||||
/// implementation, and those cannot be added by external code due to orphan rules.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@ -170,29 +219,121 @@ pub trait AsRef<T: ?Sized> {
|
||||
///
|
||||
/// # Generic Implementations
|
||||
///
|
||||
/// - `AsMut` auto-dereferences if the inner type is a mutable reference
|
||||
/// (e.g.: `foo.as_mut()` will work the same if `foo` has type `&mut Foo`
|
||||
/// or `&mut &mut Foo`)
|
||||
/// `AsMut` auto-dereferences if the inner type is a mutable reference
|
||||
/// (e.g.: `foo.as_mut()` will work the same if `foo` has type `&mut Foo` or `&mut &mut Foo`).
|
||||
///
|
||||
/// Note that due to historic reasons, the above currently does not hold generally for all
|
||||
/// [mutably dereferenceable types], e.g. `foo.as_mut()` will *not* work the same as
|
||||
/// `Box::new(foo).as_mut()`. Instead, many smart pointers provide an `as_mut` implementation which
|
||||
/// simply returns a reference to the [pointed-to value] (but do not perform a cheap
|
||||
/// reference-to-reference conversion for that value). However, [`AsMut::as_mut`] should not be
|
||||
/// used for the sole purpose of mutable dereferencing; instead ['`Deref` coercion'] can be used:
|
||||
///
|
||||
/// [mutably dereferenceable types]: core::ops::DerefMut
|
||||
/// [pointed-to value]: core::ops::Deref::Target
|
||||
/// ['`Deref` coercion']: core::ops::DerefMut#more-on-deref-coercion
|
||||
///
|
||||
/// ```
|
||||
/// let mut x = Box::new(5i32);
|
||||
/// // Avoid this:
|
||||
/// // let y: &mut i32 = x.as_mut();
|
||||
/// // Better just write:
|
||||
/// let y: &mut i32 = &mut x;
|
||||
/// ```
|
||||
///
|
||||
/// Types which implement [`DerefMut`](core::ops::DerefMut) should consider to add an
|
||||
/// implementation of `AsMut` as follows:
|
||||
///
|
||||
/// ```
|
||||
/// impl<T> AsMut<T> for SomeType
|
||||
/// where
|
||||
/// <SomeType as Deref>::Target: AsMut<T>,
|
||||
/// {
|
||||
/// fn as_mut(&mut self) -> &mut T {
|
||||
/// self.deref_mut().as_mut()
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// # Reflexivity
|
||||
///
|
||||
/// Ideally, `AsMut` would be reflexive, that is there is an `impl<T: ?Sized> AsMut<T> for T`, with
|
||||
/// [`as_mut`][AsMut::as_mut] simply returning its argument unchanged.
|
||||
/// Such a blanket implementation is currently *not* provided due to technical restrictions of
|
||||
/// Rust's type system (it would be overlapping with another existing blanket implementation for
|
||||
/// `&mut T where T: AsMut<U>` which allows `AsMut` to auto-dereference, see "Generic
|
||||
/// Implementations" above).
|
||||
///
|
||||
/// A trivial implementation of `AsMut<T> for T` must be added explicitly for a particular type `T`
|
||||
/// where needed or desired. Note, however, that not all types from `std` contain such an
|
||||
/// implementation, and those cannot be added by external code due to orphan rules.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Using `AsMut` as trait bound for a generic function we can accept all mutable references
|
||||
/// that can be converted to type `&mut T`. Because [`Box<T>`] implements `AsMut<T>` we can
|
||||
/// write a function `add_one` that takes all arguments that can be converted to `&mut u64`.
|
||||
/// Because [`Box<T>`] implements `AsMut<T>`, `add_one` accepts arguments of type
|
||||
/// `&mut Box<u64>` as well:
|
||||
/// Using `AsMut` as trait bound for a generic function, we can accept all mutable references that
|
||||
/// can be converted to type `&mut T`. Unlike [dereference], which has a single [target type],
|
||||
/// there can be multiple implementations of `AsMut` for a type. In particular, `Vec<T>` implements
|
||||
/// both `AsMut<Vec<T>>` and `AsMut<[T]>`.
|
||||
///
|
||||
/// In the following, the example functions `caesar` and `null_terminate` provide a generic
|
||||
/// interface which work with any type that can be converted by cheap mutable-to-mutable conversion
|
||||
/// into a byte slice or byte `Vec`, respectively.
|
||||
///
|
||||
/// [dereference]: core::ops::DerefMut
|
||||
/// [target type]: core::ops::Deref::Target
|
||||
///
|
||||
/// ```
|
||||
/// fn add_one<T: AsMut<u64>>(num: &mut T) {
|
||||
/// *num.as_mut() += 1;
|
||||
/// struct Document {
|
||||
/// info: String,
|
||||
/// content: Vec<u8>,
|
||||
/// }
|
||||
///
|
||||
/// let mut boxed_num = Box::new(0);
|
||||
/// add_one(&mut boxed_num);
|
||||
/// assert_eq!(*boxed_num, 1);
|
||||
/// impl<T: ?Sized> AsMut<T> for Document
|
||||
/// where
|
||||
/// Vec<u8>: AsMut<T>,
|
||||
/// {
|
||||
/// fn as_mut(&mut self) -> &mut T {
|
||||
/// self.content.as_mut()
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// fn caesar<T: AsMut<[u8]>>(data: &mut T, key: u8) {
|
||||
/// for byte in data.as_mut() {
|
||||
/// *byte = byte.wrapping_add(key);
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// fn null_terminate<T: AsMut<Vec<u8>>>(data: &mut T) {
|
||||
/// // Using a non-generic inner function, which contains most of the
|
||||
/// // functionality, helps to minimize monomorphization overhead.
|
||||
/// fn doit(data: &mut Vec<u8>) {
|
||||
/// let len = data.len();
|
||||
/// if len == 0 || data[len-1] != 0 {
|
||||
/// data.push(0);
|
||||
/// }
|
||||
/// }
|
||||
/// doit(data.as_mut());
|
||||
/// }
|
||||
///
|
||||
/// fn main() {
|
||||
/// let mut v: Vec<u8> = vec![1, 2, 3];
|
||||
/// caesar(&mut v, 5);
|
||||
/// assert_eq!(v, [6, 7, 8]);
|
||||
/// null_terminate(&mut v);
|
||||
/// assert_eq!(v, [6, 7, 8, 0]);
|
||||
/// let mut doc = Document {
|
||||
/// info: String::from("Example"),
|
||||
/// content: vec![17, 19, 8],
|
||||
/// };
|
||||
/// caesar(&mut doc, 1);
|
||||
/// assert_eq!(doc.content, [18, 20, 9]);
|
||||
/// null_terminate(&mut doc);
|
||||
/// assert_eq!(doc.content, [18, 20, 9, 0]);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// [`Box<T>`]: ../../std/boxed/struct.Box.html
|
||||
/// Note, however, that APIs don't need to be generic. In many cases taking a `&mut [u8]` or
|
||||
/// `&mut Vec<u8>`, for example, is the better choice (callers need to pass the correct type then).
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[cfg_attr(not(test), rustc_diagnostic_item = "AsMut")]
|
||||
pub trait AsMut<T: ?Sized> {
|
||||
|
Loading…
Reference in New Issue
Block a user