From 8adec1b077e96c499e5478cfbad697f22fe91e93 Mon Sep 17 00:00:00 2001 From: Dimitri Sabadie Date: Tue, 21 Jan 2025 12:13:43 +0100 Subject: [PATCH] Introduce Itertools::owned() / Owned. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is very similar to std’s `Iterator::clone()` / `Cloned`, but works on more types, such as `CStr` / `CString`. --- benches/specializations.rs | 8 ++++++ src/adaptors/mod.rs | 50 ++++++++++++++++++++++++++++++++++++++ src/lib.rs | 12 +++++++++ tests/test_std.rs | 14 +++++++++++ 4 files changed, 84 insertions(+) diff --git a/benches/specializations.rs b/benches/specializations.rs index e70323f8e..05eb5a04f 100644 --- a/benches/specializations.rs +++ b/benches/specializations.rs @@ -1,5 +1,7 @@ #![allow(unstable_name_collisions)] +use std::ffi::CString; + use criterion::black_box; use criterion::BenchmarkId; use itertools::Itertools; @@ -666,4 +668,10 @@ bench_specializations! { } v.iter().copied().flatten_ok() } + owned { + { + let v = black_box((0..1024).map(|_| CString::new("foo bar zoo").unwrap()).collect_vec()); + } + v.iter().map(CString::as_c_str).owned() + } } diff --git a/src/adaptors/mod.rs b/src/adaptors/mod.rs index 6de2ccb09..12fca9cbe 100644 --- a/src/adaptors/mod.rs +++ b/src/adaptors/mod.rs @@ -1263,3 +1263,53 @@ where F: FnMut(&mut I::Item), { } + +/// An iterator adapter to transform borrowed items ([`ToOwned`]) into their owned counterparts. +/// +/// See [`.owned()`](crate::Itertools::owned) for more information. +#[derive(Clone, Debug)] +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct Owned { + iter: I, +} + +impl<'a, I, T> Iterator for Owned +where + I: Iterator, + T: 'a + ?Sized + ToOwned, +{ + type Item = T::Owned; + + fn next(&mut self) -> Option { + self.iter.next().map(ToOwned::to_owned) + } +} + +impl<'a, I, T> ExactSizeIterator for Owned +where + I: ExactSizeIterator, + T: 'a + ?Sized + ToOwned, +{ +} + +impl<'a, I, T> DoubleEndedIterator for Owned +where + I: DoubleEndedIterator, + T: 'a + ?Sized + ToOwned, +{ + fn next_back(&mut self) -> Option { + self.iter.next_back().map(ToOwned::to_owned) + } +} + +impl<'a, I, T> FusedIterator for Owned +where + I: FusedIterator, + T: 'a + ?Sized + ToOwned, +{ +} + +/// Create a new `Owned` iterator. +pub fn owned(iter: I) -> Owned { + Owned { iter } +} diff --git a/src/lib.rs b/src/lib.rs index cba3ad570..edf69c001 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,6 +57,7 @@ extern crate core as std; #[cfg(feature = "use_alloc")] extern crate alloc; +use adaptors::Owned; #[cfg(feature = "use_alloc")] use alloc::{collections::VecDeque, string::String, vec::Vec}; @@ -91,6 +92,8 @@ pub use std::iter as __std_iter; pub mod structs { #[cfg(feature = "use_alloc")] pub use crate::adaptors::MultiProduct; + #[cfg(feature = "use_alloc")] + pub use crate::adaptors::Owned; pub use crate::adaptors::{ Batching, Coalesce, Dedup, DedupBy, DedupByWithCount, DedupWithCount, FilterMapOk, FilterOk, Interleave, InterleaveShortest, MapInto, MapOk, Positions, Product, PutBack, @@ -4581,6 +4584,15 @@ pub trait Itertools: Iterator { _ => Err(sh), } } + + /// Create a new iterator that transforms borrowed items of an underlying iterator into their owned counterparts. + #[cfg(feature = "use_alloc")] + fn owned(self) -> Owned + where + Self: Sized, + { + adaptors::owned(self) + } } impl Itertools for T where T: Iterator + ?Sized {} diff --git a/tests/test_std.rs b/tests/test_std.rs index ad391faad..5845732c4 100644 --- a/tests/test_std.rs +++ b/tests/test_std.rs @@ -20,6 +20,7 @@ use rand::{ Rng, SeedableRng, }; use rand::{seq::SliceRandom, thread_rng}; +use std::ffi::CString; use std::{cmp::min, fmt::Debug, marker::PhantomData}; #[test] @@ -1567,3 +1568,16 @@ fn multiunzip() { ) ); } + +#[test] +fn owned() { + let owned = [ + CString::new("foo").unwrap(), + CString::new("bar").unwrap(), + CString::new("").unwrap(), + CString::new("zoo").unwrap(), + ]; + let borrowed = owned.iter().map(CString::as_c_str); + let iter = borrowed.owned(); + assert_eq!(iter.collect_array(), Some(owned)); +}