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<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 }
+}
diff --git a/src/lib.rs b/src/lib.rs
index cba3ad570..3f965b0dc 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -91,6 +91,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 +4583,21 @@ pub trait Itertools: Iterator {
             _ => Err(sh),
         }
     }
+
+    /// Create a new iterator that transforms borrowed items of an underlying iterator into their owned counterparts.
+    ///
+    /// ```
+    /// use itertools::Itertools;
+    ///
+    /// assert_eq!(["Hello", " ", "world!"].iter().owned().collect_vec(), vec!["Hello".to_owned(), " ".to_owned(), "world!".to_owned()]);
+    /// ```
+    #[cfg(feature = "use_alloc")]
+    fn owned(self) -> Owned<Self>
+    where
+        Self: Sized,
+    {
+        adaptors::owned(self)
+    }
 }
 
 impl<T> 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));
+}