Skip to content

Commit a7be40c

Browse files
committed
Auto merge of #56534 - xfix:copied, r=@SimonSapin
Add unstable Iterator::copied() Initially suggested at rust-itertools/itertools#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.
2 parents 79d8a0f + 315401d commit a7be40c

File tree

6 files changed

+212
-1
lines changed

6 files changed

+212
-1
lines changed

src/libcore/iter/iterator.rs

+30-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use cmp::Ordering;
22
use ops::Try;
33

44
use super::LoopState;
5-
use super::{Chain, Cycle, Cloned, Enumerate, Filter, FilterMap, Fuse};
5+
use super::{Chain, Cycle, Copied, Cloned, Enumerate, Filter, FilterMap, Fuse};
66
use super::{Flatten, FlatMap, flatten_compat};
77
use super::{Inspect, Map, Peekable, Scan, Skip, SkipWhile, StepBy, Take, TakeWhile, Rev};
88
use super::{Zip, Sum, Product};
@@ -2225,6 +2225,35 @@ pub trait Iterator {
22252225
(ts, us)
22262226
}
22272227

2228+
/// Creates an iterator which copies all of its elements.
2229+
///
2230+
/// This is useful when you have an iterator over `&T`, but you need an
2231+
/// iterator over `T`.
2232+
///
2233+
/// # Examples
2234+
///
2235+
/// Basic usage:
2236+
///
2237+
/// ```
2238+
/// #![feature(iter_copied)]
2239+
///
2240+
/// let a = [1, 2, 3];
2241+
///
2242+
/// let v_cloned: Vec<_> = a.iter().copied().collect();
2243+
///
2244+
/// // copied is the same as .map(|&x| x)
2245+
/// let v_map: Vec<_> = a.iter().map(|&x| x).collect();
2246+
///
2247+
/// assert_eq!(v_cloned, vec![1, 2, 3]);
2248+
/// assert_eq!(v_map, vec![1, 2, 3]);
2249+
/// ```
2250+
#[unstable(feature = "iter_copied", issue = "57127")]
2251+
fn copied<'a, T: 'a>(self) -> Copied<Self>
2252+
where Self: Sized + Iterator<Item=&'a T>, T: Copy
2253+
{
2254+
Copied { it: self }
2255+
}
2256+
22282257
/// Creates an iterator which [`clone`]s all of its elements.
22292258
///
22302259
/// This is useful when you have an iterator over `&T`, but you need an

src/libcore/iter/mod.rs

+100
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,106 @@ impl<I> FusedIterator for Rev<I>
497497
unsafe impl<I> TrustedLen for Rev<I>
498498
where I: TrustedLen + DoubleEndedIterator {}
499499

500+
/// An iterator that copies the elements of an underlying iterator.
501+
///
502+
/// This `struct` is created by the [`copied`] method on [`Iterator`]. See its
503+
/// documentation for more.
504+
///
505+
/// [`copied`]: trait.Iterator.html#method.copied
506+
/// [`Iterator`]: trait.Iterator.html
507+
#[unstable(feature = "iter_copied", issue = "57127")]
508+
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
509+
#[derive(Clone, Debug)]
510+
pub struct Copied<I> {
511+
it: I,
512+
}
513+
514+
#[unstable(feature = "iter_copied", issue = "57127")]
515+
impl<'a, I, T: 'a> Iterator for Copied<I>
516+
where I: Iterator<Item=&'a T>, T: Copy
517+
{
518+
type Item = T;
519+
520+
fn next(&mut self) -> Option<T> {
521+
self.it.next().copied()
522+
}
523+
524+
fn size_hint(&self) -> (usize, Option<usize>) {
525+
self.it.size_hint()
526+
}
527+
528+
fn try_fold<B, F, R>(&mut self, init: B, mut f: F) -> R where
529+
Self: Sized, F: FnMut(B, Self::Item) -> R, R: Try<Ok=B>
530+
{
531+
self.it.try_fold(init, move |acc, &elt| f(acc, elt))
532+
}
533+
534+
fn fold<Acc, F>(self, init: Acc, mut f: F) -> Acc
535+
where F: FnMut(Acc, Self::Item) -> Acc,
536+
{
537+
self.it.fold(init, move |acc, &elt| f(acc, elt))
538+
}
539+
}
540+
541+
#[unstable(feature = "iter_copied", issue = "57127")]
542+
impl<'a, I, T: 'a> DoubleEndedIterator for Copied<I>
543+
where I: DoubleEndedIterator<Item=&'a T>, T: Copy
544+
{
545+
fn next_back(&mut self) -> Option<T> {
546+
self.it.next_back().copied()
547+
}
548+
549+
fn try_rfold<B, F, R>(&mut self, init: B, mut f: F) -> R where
550+
Self: Sized, F: FnMut(B, Self::Item) -> R, R: Try<Ok=B>
551+
{
552+
self.it.try_rfold(init, move |acc, &elt| f(acc, elt))
553+
}
554+
555+
fn rfold<Acc, F>(self, init: Acc, mut f: F) -> Acc
556+
where F: FnMut(Acc, Self::Item) -> Acc,
557+
{
558+
self.it.rfold(init, move |acc, &elt| f(acc, elt))
559+
}
560+
}
561+
562+
#[unstable(feature = "iter_copied", issue = "57127")]
563+
impl<'a, I, T: 'a> ExactSizeIterator for Copied<I>
564+
where I: ExactSizeIterator<Item=&'a T>, T: Copy
565+
{
566+
fn len(&self) -> usize {
567+
self.it.len()
568+
}
569+
570+
fn is_empty(&self) -> bool {
571+
self.it.is_empty()
572+
}
573+
}
574+
575+
#[unstable(feature = "iter_copied", issue = "57127")]
576+
impl<'a, I, T: 'a> FusedIterator for Copied<I>
577+
where I: FusedIterator<Item=&'a T>, T: Copy
578+
{}
579+
580+
#[doc(hidden)]
581+
unsafe impl<'a, I, T: 'a> TrustedRandomAccess for Copied<I>
582+
where I: TrustedRandomAccess<Item=&'a T>, T: Copy
583+
{
584+
unsafe fn get_unchecked(&mut self, i: usize) -> Self::Item {
585+
*self.it.get_unchecked(i)
586+
}
587+
588+
#[inline]
589+
fn may_have_side_effect() -> bool {
590+
I::may_have_side_effect()
591+
}
592+
}
593+
594+
#[unstable(feature = "iter_copied", issue = "57127")]
595+
unsafe impl<'a, I, T: 'a> TrustedLen for Copied<I>
596+
where I: TrustedLen<Item=&'a T>,
597+
T: Copy
598+
{}
599+
500600
/// An iterator that clones the elements of an underlying iterator.
501601
///
502602
/// This `struct` is created by the [`cloned`] method on [`Iterator`]. See its

src/libcore/option.rs

+42
Original file line numberDiff line numberDiff line change
@@ -874,6 +874,48 @@ impl<T> Option<T> {
874874
}
875875
}
876876

877+
impl<'a, T: Copy> Option<&'a T> {
878+
/// Maps an `Option<&T>` to an `Option<T>` by copying the contents of the
879+
/// option.
880+
///
881+
/// # Examples
882+
///
883+
/// ```
884+
/// #![feature(copied)]
885+
///
886+
/// let x = 12;
887+
/// let opt_x = Some(&x);
888+
/// assert_eq!(opt_x, Some(&12));
889+
/// let copied = opt_x.copied();
890+
/// assert_eq!(copied, Some(12));
891+
/// ```
892+
#[unstable(feature = "copied", issue = "57126")]
893+
pub fn copied(self) -> Option<T> {
894+
self.map(|&t| t)
895+
}
896+
}
897+
898+
impl<'a, T: Copy> Option<&'a mut T> {
899+
/// Maps an `Option<&mut T>` to an `Option<T>` by copying the contents of the
900+
/// option.
901+
///
902+
/// # Examples
903+
///
904+
/// ```
905+
/// #![feature(copied)]
906+
///
907+
/// let mut x = 12;
908+
/// let opt_x = Some(&mut x);
909+
/// assert_eq!(opt_x, Some(&mut 12));
910+
/// let copied = opt_x.copied();
911+
/// assert_eq!(copied, Some(12));
912+
/// ```
913+
#[unstable(feature = "copied", issue = "57126")]
914+
pub fn copied(self) -> Option<T> {
915+
self.map(|&mut t| t)
916+
}
917+
}
918+
877919
impl<'a, T: Clone> Option<&'a T> {
878920
/// Maps an `Option<&T>` to an `Option<T>` by cloning the contents of the
879921
/// option.

src/libcore/tests/iter.rs

+17
Original file line numberDiff line numberDiff line change
@@ -1253,6 +1253,23 @@ fn test_rev() {
12531253
vec![16, 14, 12, 10, 8, 6]);
12541254
}
12551255

1256+
#[test]
1257+
fn test_copied() {
1258+
let xs = [2, 4, 6, 8];
1259+
1260+
let mut it = xs.iter().copied();
1261+
assert_eq!(it.len(), 4);
1262+
assert_eq!(it.next(), Some(2));
1263+
assert_eq!(it.len(), 3);
1264+
assert_eq!(it.next(), Some(4));
1265+
assert_eq!(it.len(), 2);
1266+
assert_eq!(it.next_back(), Some(8));
1267+
assert_eq!(it.len(), 1);
1268+
assert_eq!(it.next_back(), Some(6));
1269+
assert_eq!(it.len(), 0);
1270+
assert_eq!(it.next_back(), None);
1271+
}
1272+
12561273
#[test]
12571274
fn test_cloned() {
12581275
let xs = [2, 4, 6, 8];

src/libcore/tests/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#![feature(box_syntax)]
22
#![feature(cell_update)]
3+
#![feature(copied)]
34
#![feature(core_private_bignum)]
45
#![feature(core_private_diy_float)]
56
#![feature(dec2flt)]
@@ -9,6 +10,7 @@
910
#![feature(flt2dec)]
1011
#![feature(fmt_internals)]
1112
#![feature(hashmap_internals)]
13+
#![feature(iter_copied)]
1214
#![feature(iter_nth_back)]
1315
#![feature(iter_unfold)]
1416
#![feature(pattern)]

src/libcore/tests/option.rs

+21
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,27 @@ fn test_collect() {
238238
assert!(v == None);
239239
}
240240

241+
#[test]
242+
fn test_copied() {
243+
let val = 1;
244+
let val_ref = &val;
245+
let opt_none: Option<&'static u32> = None;
246+
let opt_ref = Some(&val);
247+
let opt_ref_ref = Some(&val_ref);
248+
249+
// None works
250+
assert_eq!(opt_none.clone(), None);
251+
assert_eq!(opt_none.copied(), None);
252+
253+
// Immutable ref works
254+
assert_eq!(opt_ref.clone(), Some(&val));
255+
assert_eq!(opt_ref.copied(), Some(1));
256+
257+
// Double Immutable ref works
258+
assert_eq!(opt_ref_ref.clone(), Some(&val_ref));
259+
assert_eq!(opt_ref_ref.clone().copied(), Some(&val));
260+
assert_eq!(opt_ref_ref.copied().copied(), Some(1));
261+
}
241262

242263
#[test]
243264
fn test_cloned() {

0 commit comments

Comments
 (0)