1
Fork 0

Auto merge of #56534 - xfix:copied, r=@SimonSapin

Add unstable Iterator::copied()

Initially suggested at https://github.com/bluss/rust-itertools/pull/289, however the maintainers of itertools suggested this may be better of in a standard library.

The intent of `copied` is to avoid accidentally cloning iterator elements after doing a code refactoring which causes a structure to be no longer `Copy`. This is a relatively common pattern, as it can be seen by calling `rg --pcre2 '[.]map[(][|](?:(\w+)[|] [*]\1|&(\w+)[|] \2)[)]'` on Rust main repository. Additionally, many uses of `cloned` actually want to simply `Copy`, and changing something to be no longer copyable may introduce unnoticeable performance penalty.

Also, this makes sense because the standard library includes `[T].copy_from_slice` to pair with `[T].clone_from_slice`.

This also adds `Option::copied`, because it makes sense to pair it with `Iterator::copied`. I don't think this feature is particularly important, but it makes sense to update `Option` along with `Iterator` for consistency.
This commit is contained in:
bors 2018-12-26 19:39:19 +00:00
commit a7be40c65a
6 changed files with 212 additions and 1 deletions

View file

@ -2,7 +2,7 @@ use cmp::Ordering;
use ops::Try;
use super::LoopState;
use super::{Chain, Cycle, Cloned, Enumerate, Filter, FilterMap, Fuse};
use super::{Chain, Cycle, Copied, Cloned, Enumerate, Filter, FilterMap, Fuse};
use super::{Flatten, FlatMap, flatten_compat};
use super::{Inspect, Map, Peekable, Scan, Skip, SkipWhile, StepBy, Take, TakeWhile, Rev};
use super::{Zip, Sum, Product};
@ -2225,6 +2225,35 @@ pub trait Iterator {
(ts, us)
}
/// Creates an iterator which copies all of its elements.
///
/// This is useful when you have an iterator over `&T`, but you need an
/// iterator over `T`.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(iter_copied)]
///
/// let a = [1, 2, 3];
///
/// let v_cloned: Vec<_> = a.iter().copied().collect();
///
/// // copied is the same as .map(|&x| x)
/// let v_map: Vec<_> = a.iter().map(|&x| x).collect();
///
/// assert_eq!(v_cloned, vec![1, 2, 3]);
/// assert_eq!(v_map, vec![1, 2, 3]);
/// ```
#[unstable(feature = "iter_copied", issue = "57127")]
fn copied<'a, T: 'a>(self) -> Copied<Self>
where Self: Sized + Iterator<Item=&'a T>, T: Copy
{
Copied { it: self }
}
/// Creates an iterator which [`clone`]s all of its elements.
///
/// This is useful when you have an iterator over `&T`, but you need an

View file

@ -497,6 +497,106 @@ impl<I> FusedIterator for Rev<I>
unsafe impl<I> TrustedLen for Rev<I>
where I: TrustedLen + DoubleEndedIterator {}
/// An iterator that copies the elements of an underlying iterator.
///
/// This `struct` is created by the [`copied`] method on [`Iterator`]. See its
/// documentation for more.
///
/// [`copied`]: trait.Iterator.html#method.copied
/// [`Iterator`]: trait.Iterator.html
#[unstable(feature = "iter_copied", issue = "57127")]
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
#[derive(Clone, Debug)]
pub struct Copied<I> {
it: I,
}
#[unstable(feature = "iter_copied", issue = "57127")]
impl<'a, I, T: 'a> Iterator for Copied<I>
where I: Iterator<Item=&'a T>, T: Copy
{
type Item = T;
fn next(&mut self) -> Option<T> {
self.it.next().copied()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.it.size_hint()
}
fn try_fold<B, F, R>(&mut self, init: B, mut f: F) -> R where
Self: Sized, F: FnMut(B, Self::Item) -> R, R: Try<Ok=B>
{
self.it.try_fold(init, move |acc, &elt| f(acc, elt))
}
fn fold<Acc, F>(self, init: Acc, mut f: F) -> Acc
where F: FnMut(Acc, Self::Item) -> Acc,
{
self.it.fold(init, move |acc, &elt| f(acc, elt))
}
}
#[unstable(feature = "iter_copied", issue = "57127")]
impl<'a, I, T: 'a> DoubleEndedIterator for Copied<I>
where I: DoubleEndedIterator<Item=&'a T>, T: Copy
{
fn next_back(&mut self) -> Option<T> {
self.it.next_back().copied()
}
fn try_rfold<B, F, R>(&mut self, init: B, mut f: F) -> R where
Self: Sized, F: FnMut(B, Self::Item) -> R, R: Try<Ok=B>
{
self.it.try_rfold(init, move |acc, &elt| f(acc, elt))
}
fn rfold<Acc, F>(self, init: Acc, mut f: F) -> Acc
where F: FnMut(Acc, Self::Item) -> Acc,
{
self.it.rfold(init, move |acc, &elt| f(acc, elt))
}
}
#[unstable(feature = "iter_copied", issue = "57127")]
impl<'a, I, T: 'a> ExactSizeIterator for Copied<I>
where I: ExactSizeIterator<Item=&'a T>, T: Copy
{
fn len(&self) -> usize {
self.it.len()
}
fn is_empty(&self) -> bool {
self.it.is_empty()
}
}
#[unstable(feature = "iter_copied", issue = "57127")]
impl<'a, I, T: 'a> FusedIterator for Copied<I>
where I: FusedIterator<Item=&'a T>, T: Copy
{}
#[doc(hidden)]
unsafe impl<'a, I, T: 'a> TrustedRandomAccess for Copied<I>
where I: TrustedRandomAccess<Item=&'a T>, T: Copy
{
unsafe fn get_unchecked(&mut self, i: usize) -> Self::Item {
*self.it.get_unchecked(i)
}
#[inline]
fn may_have_side_effect() -> bool {
I::may_have_side_effect()
}
}
#[unstable(feature = "iter_copied", issue = "57127")]
unsafe impl<'a, I, T: 'a> TrustedLen for Copied<I>
where I: TrustedLen<Item=&'a T>,
T: Copy
{}
/// An iterator that clones the elements of an underlying iterator.
///
/// This `struct` is created by the [`cloned`] method on [`Iterator`]. See its

View file

@ -874,6 +874,48 @@ impl<T> Option<T> {
}
}
impl<'a, T: Copy> Option<&'a T> {
/// Maps an `Option<&T>` to an `Option<T>` by copying the contents of the
/// option.
///
/// # Examples
///
/// ```
/// #![feature(copied)]
///
/// let x = 12;
/// let opt_x = Some(&x);
/// assert_eq!(opt_x, Some(&12));
/// let copied = opt_x.copied();
/// assert_eq!(copied, Some(12));
/// ```
#[unstable(feature = "copied", issue = "57126")]
pub fn copied(self) -> Option<T> {
self.map(|&t| t)
}
}
impl<'a, T: Copy> Option<&'a mut T> {
/// Maps an `Option<&mut T>` to an `Option<T>` by copying the contents of the
/// option.
///
/// # Examples
///
/// ```
/// #![feature(copied)]
///
/// let mut x = 12;
/// let opt_x = Some(&mut x);
/// assert_eq!(opt_x, Some(&mut 12));
/// let copied = opt_x.copied();
/// assert_eq!(copied, Some(12));
/// ```
#[unstable(feature = "copied", issue = "57126")]
pub fn copied(self) -> Option<T> {
self.map(|&mut t| t)
}
}
impl<'a, T: Clone> Option<&'a T> {
/// Maps an `Option<&T>` to an `Option<T>` by cloning the contents of the
/// option.

View file

@ -1253,6 +1253,23 @@ fn test_rev() {
vec![16, 14, 12, 10, 8, 6]);
}
#[test]
fn test_copied() {
let xs = [2, 4, 6, 8];
let mut it = xs.iter().copied();
assert_eq!(it.len(), 4);
assert_eq!(it.next(), Some(2));
assert_eq!(it.len(), 3);
assert_eq!(it.next(), Some(4));
assert_eq!(it.len(), 2);
assert_eq!(it.next_back(), Some(8));
assert_eq!(it.len(), 1);
assert_eq!(it.next_back(), Some(6));
assert_eq!(it.len(), 0);
assert_eq!(it.next_back(), None);
}
#[test]
fn test_cloned() {
let xs = [2, 4, 6, 8];

View file

@ -1,5 +1,6 @@
#![feature(box_syntax)]
#![feature(cell_update)]
#![feature(copied)]
#![feature(core_private_bignum)]
#![feature(core_private_diy_float)]
#![feature(dec2flt)]
@ -9,6 +10,7 @@
#![feature(flt2dec)]
#![feature(fmt_internals)]
#![feature(hashmap_internals)]
#![feature(iter_copied)]
#![feature(iter_nth_back)]
#![feature(iter_unfold)]
#![feature(pattern)]

View file

@ -238,6 +238,27 @@ fn test_collect() {
assert!(v == None);
}
#[test]
fn test_copied() {
let val = 1;
let val_ref = &val;
let opt_none: Option<&'static u32> = None;
let opt_ref = Some(&val);
let opt_ref_ref = Some(&val_ref);
// None works
assert_eq!(opt_none.clone(), None);
assert_eq!(opt_none.copied(), None);
// Immutable ref works
assert_eq!(opt_ref.clone(), Some(&val));
assert_eq!(opt_ref.copied(), Some(1));
// Double Immutable ref works
assert_eq!(opt_ref_ref.clone(), Some(&val_ref));
assert_eq!(opt_ref_ref.clone().copied(), Some(&val));
assert_eq!(opt_ref_ref.copied().copied(), Some(1));
}
#[test]
fn test_cloned() {