]> git.lizzy.rs Git - rust.git/commitdiff
Rollup merge of #99460 - JanBeh:PR_asref_asmut_docs, r=joshtriplett
authorMatthias Krüger <matthias.krueger@famsik.de>
Mon, 3 Oct 2022 18:58:54 +0000 (20:58 +0200)
committerGitHub <noreply@github.com>
Mon, 3 Oct 2022 18:58:54 +0000 (20:58 +0200)
docs: Improve AsRef / AsMut docs on blanket impls

There are several issues with the current state of `AsRef` and `AsMut` as [discussed here on IRLO](https://internals.rust-lang.org/t/semantics-of-asref/17016). See also #39397, #45742, #73390, #98905, and the FIXMEs [here](https://github.com/rust-lang/rust/blob/1.62.0/library/core/src/convert/mod.rs#L509-L515) and [here](https://github.com/rust-lang/rust/blob/1.62.0/library/core/src/convert/mod.rs#L530-L536). These issues are difficult to fix. This PR aims to update the documentation to better reflect the status-quo and to give advice on how `AsRef` and `AsMut` should be used.

In particular:

- 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

1  2 
library/core/src/convert/mod.rs

index 6f23b9d908dba63ca67325e7bf6fce242e0dc488,e2be262e311d6072f428e9fc778f30e6c7e18c5c..223695c2bec7b48b893358d455e0052e7a283cef
@@@ -178,26 -298,72 +300,73 @@@ pub trait AsRef<T: ?Sized> 
  ///
  /// # 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 (`[u8]`) or byte vector (`Vec<u8>`), 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>,
+ /// }
+ ///
+ /// impl<T: ?Sized> AsMut<T> for Document
+ /// where
+ ///     Vec<u8>: AsMut<T>,
+ /// {
+ ///     fn as_mut(&mut self) -> &mut T {
+ ///         self.content.as_mut()
+ ///     }
  /// }
  ///
- /// let mut boxed_num = Box::new(0);
- /// add_one(&mut boxed_num);
- /// assert_eq!(*boxed_num, 1);
+ /// 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")]
 +#[const_trait]
  pub trait AsMut<T: ?Sized> {
      /// Converts this type into a mutable reference of the (usually inferred) input type.
      #[stable(feature = "rust1", since = "1.0.0")]