From 4038042eb0f9e8088974f1d9342103ed1b5de31f Mon Sep 17 00:00:00 2001 From: Lukas Kalbertodt Date: Thu, 13 Aug 2020 00:19:53 +0200 Subject: [PATCH] Add `[T; N]::each_ref` and `[T; N]::each_mut` These methods work very similarly to `Option`'s methods `as_ref` and `as_mut`. They are useful in several situation, particularly when calling other array methods (like `map`) on the result. Unfortunately, we can't easily call them `as_ref` and `as_mut` as that would shadow those methods on slices, thus being a breaking change (that is likely to affect a lot of code). --- library/core/src/array/mod.rs | 75 +++++++++++++++++++++++++++++++++-- 1 file changed, 72 insertions(+), 3 deletions(-) diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 71548bec7aaee..85b1a47f4a9ff 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; N] { where F: FnMut(T) -> U, { - use crate::mem::MaybeUninit; struct Guard { dst: *mut T, initialized: usize, @@ -481,8 +481,6 @@ impl [T; N] { /// ``` #[unstable(feature = "array_zip", issue = "80094")] pub fn zip(self, rhs: [U; N]) -> [(T, U); N] { - use crate::mem::MaybeUninit; - let mut dst = MaybeUninit::uninit_array::(); for (i, (lhs, rhs)) in IntoIter::new(self).zip(IntoIter::new(rhs)).enumerate() { dst[i].write((lhs, rhs)); @@ -506,4 +504,75 @@ impl [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::(); + for (src, dst) in self.iter().zip(&mut out) { + dst.write(src); + } + + // SAFETY: All elements of `dst` are properly initialized and + // `MaybeUninit` 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::(); + for (src, dst) in self.iter_mut().zip(&mut out) { + dst.write(src); + } + + // SAFETY: All elements of `dst` are properly initialized and + // `MaybeUninit` has the same layout as `T`, so this cast is valid. + unsafe { (&mut out as *mut _ as *mut [&mut T; N]).read() } + } }