diff --git a/src/impls/string.rs b/src/impls/string.rs index b6aabb1..d54e1f4 100644 --- a/src/impls/string.rs +++ b/src/impls/string.rs @@ -167,7 +167,7 @@ where #[cfg(test)] mod tests { - use crate::{Push, Region, ReserveItems, StringRegion}; + use crate::{Push, ReadToOwned, Region, ReserveItems, StringRegion}; #[test] fn test_inner() { @@ -220,4 +220,15 @@ mod tests { assert!(cap > 0); assert!(cnt > 0); } + + #[test] + fn owned() { + let mut r = ::default(); + + let idx = r.push("abc"); + let reference = r.index(idx); + let owned = reference.read_to_owned(); + let idx = r.push(owned); + assert_eq!("abc", r.index(idx)); + } } diff --git a/src/lib.rs b/src/lib.rs index 561b878..95fa983 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -316,6 +316,35 @@ impl Clone for FlatStack { #[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd)] pub struct CopyIter(pub I); +/// Conversion of references to owned data. Similar to [`ToOwned`], but without the requirement +/// that target can be borrowed to self. +/// +/// The clumsy names originate from `to_owned` already being in scope. +pub trait ReadToOwned { + /// The owned type. Static lifetime to indicate that the lifetime of the owned object must not + /// depend on self. + type Owned: 'static; + /// Convert self into an owned representation. + fn read_to_owned(self) -> Self::Owned; + /// Convert self into an owned representation, re-using an existing allocation. + fn read_to_owned_into(self, target: &mut Self::Owned); +} + +impl ReadToOwned for &T +where + T::Owned: 'static, +{ + type Owned = T::Owned; + + fn read_to_owned(self) -> Self::Owned { + self.to_owned() + } + + fn read_to_owned_into(self, target: &mut Self::Owned) { + self.clone_into(target); + } +} + #[cfg(test)] mod tests { use crate::impls::deduplicate::{CollapseSequence, ConsecutiveOffsetPairs}; @@ -393,6 +422,26 @@ mod tests { hobbies: < as Containerized>::Region as Region>::ReadItem<'a>, } + impl ReadToOwned for PersonRef<'_> { + type Owned = Person; + fn read_to_owned(self) -> Person { + Person { + name: self.name.to_string(), + age: self.age, + hobbies: self.hobbies.iter().map(|s| s.to_string()).collect(), + } + } + fn read_to_owned_into(self, target: &mut Person) { + target.name.clear(); + target.name.push_str(self.name); + target.age = self.age; + target.hobbies.clear(); + target + .hobbies + .extend(self.hobbies.iter().map(str::to_string)); + } + } + impl Region for PersonRegion { type ReadItem<'a> = PersonRef<'a> where Self: 'a; type Index = ( @@ -681,4 +730,20 @@ mod tests { c.copy([[[vec![[&1; 1]; 1]; 1]; 1]; 1]); c.copy([[&vec![[[&1; 1]; 1]; 1]; 1]; 1]); } + + fn owned_roundtrip(region: &mut R, index: R::Index) + where + for<'a> R: Region + Push<<::ReadItem<'a> as ReadToOwned>::Owned>, + for<'a> R::ReadItem<'a>: ReadToOwned, + { + let item = region.index(index).read_to_owned(); + region.push(item); + } + + #[test] + fn test_owned() { + let mut c = ::default(); + let index = c.push("abc".to_string()); + owned_roundtrip(&mut c, index); + } }