From 81e8da218b8269f1dbcf8932c4b85a3bf6f5389b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 8 Sep 2021 13:14:56 -0700 Subject: [PATCH 1/2] Smarter default implementation of Extend::extend_reserve --- library/core/src/iter/traits/collect.rs | 33 +++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/library/core/src/iter/traits/collect.rs b/library/core/src/iter/traits/collect.rs index 56fad602cf9c8..4df8c5212e898 100644 --- a/library/core/src/iter/traits/collect.rs +++ b/library/core/src/iter/traits/collect.rs @@ -1,3 +1,5 @@ +use crate::marker::PhantomData; + /// Conversion from an [`Iterator`]. /// /// By implementing `FromIterator` for a type, you define how it will be @@ -346,10 +348,37 @@ pub trait Extend { /// Reserves capacity in a collection for the given number of additional elements. /// - /// The default implementation does nothing. + /// The default implementation forwards to `extend` with an iterator having + /// the given requested capacity as `size_hint`, which has the effect of + /// reserving if `extend` takes into account its argument's size hint in + /// order to reserve capacity. #[unstable(feature = "extend_one", issue = "72631")] fn extend_reserve(&mut self, additional: usize) { - let _ = additional; + struct ReserveHint { + additional: usize, + item: PhantomData, + } + + // DO NOT implement TrustedLen, because the real len is actually 0. + impl Iterator for ReserveHint { + type Item = A; + + fn next(&mut self) -> Option { + None + } + + fn size_hint(&self) -> (usize, Option) { + (self.additional, Some(self.additional)) + } + } + + impl ExactSizeIterator for ReserveHint { + fn len(&self) -> usize { + self.additional + } + } + + self.extend(ReserveHint { additional, item: PhantomData }) } } From c75947b01725a6e3a9f1a32900ff6a0541ebed5e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 8 Sep 2021 13:24:22 -0700 Subject: [PATCH 2/2] Add test of extend_reserve default impl --- library/core/tests/iter/traits/collect.rs | 27 +++++++++++++++++++++++ library/core/tests/iter/traits/mod.rs | 1 + library/core/tests/lib.rs | 1 + 3 files changed, 29 insertions(+) create mode 100644 library/core/tests/iter/traits/collect.rs diff --git a/library/core/tests/iter/traits/collect.rs b/library/core/tests/iter/traits/collect.rs new file mode 100644 index 0000000000000..d50f6d81d438c --- /dev/null +++ b/library/core/tests/iter/traits/collect.rs @@ -0,0 +1,27 @@ +#[test] +fn test_extend_reserve() { + struct Collection { + len: usize, + capacity: usize, + } + + impl Extend for Collection { + fn extend>(&mut self, elements: I) { + let iter = elements.into_iter(); + let (lower_bound, _) = iter.size_hint(); + let expected_len = self.len.saturating_add(lower_bound); + if self.capacity < expected_len { + // do the reserve + self.capacity = expected_len; + } + // do the extend + iter.into_iter().for_each(drop); + } + + // no custom implementation of extend_reserve + } + + let mut collection = Collection { len: 0, capacity: 0 }; + collection.extend_reserve(5); + assert_eq!(collection.capacity, 5); +} diff --git a/library/core/tests/iter/traits/mod.rs b/library/core/tests/iter/traits/mod.rs index 80619f53f25f9..b00e49b06967f 100644 --- a/library/core/tests/iter/traits/mod.rs +++ b/library/core/tests/iter/traits/mod.rs @@ -1,4 +1,5 @@ mod accum; +mod collect; mod double_ended; mod iterator; mod step; diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 19bcc45108dfd..f2730fdddcf85 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -23,6 +23,7 @@ #![feature(duration_consts_2)] #![feature(duration_constants)] #![feature(exact_size_is_empty)] +#![feature(extend_one)] #![feature(extern_types)] #![feature(flt2dec)] #![feature(fmt_internals)]