about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2021-01-11 18:50:53 +0000
committerbors <bors@rust-lang.org>2021-01-11 18:50:53 +0000
commitc5eae562935922f712edec56a45591bc2f8ded1c (patch)
tree35af8ef473d32f7e42de1dfe9cf423f7b97f9b35
parent6526e5c772f2da07db745c94ca6bb0a591a39ba4 (diff)
parent4038042eb0f9e8088974f1d9342103ed1b5de31f (diff)
downloadrust-c5eae562935922f712edec56a45591bc2f8ded1c.tar.gz
rust-c5eae562935922f712edec56a45591bc2f8ded1c.zip
Auto merge of #75490 - LukasKalbertodt:add-basic-array-methods, r=dtolnay
Add `[T; N]::each_ref` and `[T; N]::each_mut`

This PR adds the methods `each_ref` and `each_mut` to `[T; N]`. The ability to add methods to arrays was added in #75212. These two methods are particularly useful with `map` which was also added in that PR. Tracking issue: #76118

```rust
impl<T, const N: usize> [T; N] {
    pub fn each_ref(&self) -> [&T; N];
    pub fn each_mut(&mut self) -> [&mut T; N];
}
```
-rw-r--r--library/core/src/array/mod.rs75
1 files changed, 72 insertions, 3 deletions
diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs
index 71548bec7aa..85b1a47f4a9 100644
--- a/library/core/src/array/mod.rs
+++ b/library/core/src/array/mod.rs
@@ -12,6 +12,7 @@ use crate::convert::{Infallible, TryFrom};
 use crate::fmt;
 use crate::hash::{self, Hash};
 use crate::marker::Unsize;
+use crate::mem::MaybeUninit;
 use crate::ops::{Index, IndexMut};
 use crate::slice::{Iter, IterMut};
 
@@ -429,7 +430,6 @@ impl<T, const N: usize> [T; N] {
     where
         F: FnMut(T) -> U,
     {
-        use crate::mem::MaybeUninit;
         struct Guard<T, const N: usize> {
             dst: *mut T,
             initialized: usize,
@@ -481,8 +481,6 @@ impl<T, const N: usize> [T; N] {
     /// ```
     #[unstable(feature = "array_zip", issue = "80094")]
     pub fn zip<U>(self, rhs: [U; N]) -> [(T, U); N] {
-        use crate::mem::MaybeUninit;
-
         let mut dst = MaybeUninit::uninit_array::<N>();
         for (i, (lhs, rhs)) in IntoIter::new(self).zip(IntoIter::new(rhs)).enumerate() {
             dst[i].write((lhs, rhs));
@@ -506,4 +504,75 @@ impl<T, const N: usize> [T; N] {
     pub fn as_mut_slice(&mut self) -> &mut [T] {
         self
     }
+
+    /// Borrows each element and returns an array of references with the same
+    /// size as `self`.
+    ///
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// #![feature(array_methods)]
+    ///
+    /// let floats = [3.1, 2.7, -1.0];
+    /// let float_refs: [&f64; 3] = floats.each_ref();
+    /// assert_eq!(float_refs, [&3.1, &2.7, &-1.0]);
+    /// ```
+    ///
+    /// This method is particularly useful if combined with other methods, like
+    /// [`map`](#method.map). This way, you can can avoid moving the original
+    /// array if its elements are not `Copy`.
+    ///
+    /// ```
+    /// #![feature(array_methods, array_map)]
+    ///
+    /// let strings = ["Ferris".to_string(), "♥".to_string(), "Rust".to_string()];
+    /// let is_ascii = strings.each_ref().map(|s| s.is_ascii());
+    /// assert_eq!(is_ascii, [true, false, true]);
+    ///
+    /// // We can still access the original array: it has not been moved.
+    /// assert_eq!(strings.len(), 3);
+    /// ```
+    #[unstable(feature = "array_methods", issue = "76118")]
+    pub fn each_ref(&self) -> [&T; N] {
+        // Unlike in `map`, we don't need a guard here, as dropping a reference
+        // is a noop.
+        let mut out = MaybeUninit::uninit_array::<N>();
+        for (src, dst) in self.iter().zip(&mut out) {
+            dst.write(src);
+        }
+
+        // SAFETY: All elements of `dst` are properly initialized and
+        // `MaybeUninit<T>` has the same layout as `T`, so this cast is valid.
+        unsafe { (&mut out as *mut _ as *mut [&T; N]).read() }
+    }
+
+    /// Borrows each element mutably and returns an array of mutable references
+    /// with the same size as `self`.
+    ///
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// #![feature(array_methods)]
+    ///
+    /// let mut floats = [3.1, 2.7, -1.0];
+    /// let float_refs: [&mut f64; 3] = floats.each_mut();
+    /// *float_refs[0] = 0.0;
+    /// assert_eq!(float_refs, [&mut 0.0, &mut 2.7, &mut -1.0]);
+    /// assert_eq!(floats, [0.0, 2.7, -1.0]);
+    /// ```
+    #[unstable(feature = "array_methods", issue = "76118")]
+    pub fn each_mut(&mut self) -> [&mut T; N] {
+        // Unlike in `map`, we don't need a guard here, as dropping a reference
+        // is a noop.
+        let mut out = MaybeUninit::uninit_array::<N>();
+        for (src, dst) in self.iter_mut().zip(&mut out) {
+            dst.write(src);
+        }
+
+        // SAFETY: All elements of `dst` are properly initialized and
+        // `MaybeUninit<T>` has the same layout as `T`, so this cast is valid.
+        unsafe { (&mut out as *mut _ as *mut [&mut T; N]).read() }
+    }
 }