Skip to content

Commit

Permalink
Introduce Itertools::owned() / Owned.
Browse files Browse the repository at this point in the history
This is very similar to std’s `Iterator::clone()` / `Cloned`, but works on more
types, such as `CStr` / `CString`.
  • Loading branch information
hadronized committed Jan 21, 2025
1 parent 25c1eff commit 8adec1b
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 0 deletions.
8 changes: 8 additions & 0 deletions benches/specializations.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#![allow(unstable_name_collisions)]

use std::ffi::CString;

use criterion::black_box;
use criterion::BenchmarkId;
use itertools::Itertools;
Expand Down Expand Up @@ -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()
}
}
50 changes: 50 additions & 0 deletions src/adaptors/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<I> {
iter: I,
}

impl<'a, I, T> Iterator for Owned<I>
where
I: Iterator<Item = &'a T>,
T: 'a + ?Sized + ToOwned,
{
type Item = T::Owned;

fn next(&mut self) -> Option<Self::Item> {
self.iter.next().map(ToOwned::to_owned)
}
}

impl<'a, I, T> ExactSizeIterator for Owned<I>
where
I: ExactSizeIterator<Item = &'a T>,
T: 'a + ?Sized + ToOwned,
{
}

impl<'a, I, T> DoubleEndedIterator for Owned<I>
where
I: DoubleEndedIterator<Item = &'a T>,
T: 'a + ?Sized + ToOwned,
{
fn next_back(&mut self) -> Option<Self::Item> {
self.iter.next_back().map(ToOwned::to_owned)
}
}

impl<'a, I, T> FusedIterator for Owned<I>
where
I: FusedIterator<Item = &'a T>,
T: 'a + ?Sized + ToOwned,
{
}

/// Create a new `Owned` iterator.
pub fn owned<I>(iter: I) -> Owned<I> {
Owned { iter }
}
12 changes: 12 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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<Self>
where
Self: Sized,
{
adaptors::owned(self)
}
}

impl<T> Itertools for T where T: Iterator + ?Sized {}
Expand Down
14 changes: 14 additions & 0 deletions tests/test_std.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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));
}

0 comments on commit 8adec1b

Please sign in to comment.