Reputation: 2244
I have a struct defined like
struct Foo<'a> {
field1: &'a str,
field2: &'a str,
field3: &'a u8,
// ...
}
that I use for returning parsing results from an mmap
ped file. For some successful parses, I want to store the results for later processing and for various reasons that processing will happen after the memory is released. I could do something like
struct OwnedFoo {
field1: String,
field2: String,
field3: Vec<u8>,
// ...
}
and manually converting all Foo
s that I'm interested in into OwnedFoos
. However I'm wondering if I could do something like:
struct Foo<'a> {
field1: Cow<'a, str>,
field2: Cow<'a, str>,
field3: Cow<'a, u8>,
...
}
instead and if there's any way to automatically make all the Cow
s owned and erase the lifetime parameter. I haven't found anything in the library documentation that seems applicable.
Something like:
let a = Foo { ... };
let a_owned = a.into_owned();
// do stuff with a_owned that I can't do with a
Upvotes: 7
Views: 2243
Reputation: 1
use std::borrow::Cow;
#[derive(Debug)]
struct Foo<'a> {
field1: &'a str,
field2: &'a str,
field3: &'a [u8],
// ...
}
#[derive(Debug)]
struct OwnedFoo {
field1: String,
field2: String,
field3: Vec<u8>,
// ...
}
#[derive(Debug)]
struct CowFoo<'a> {
field1: Cow<'a, str>,
field2: Cow<'a, str>,
// field3: Cow<'a, [u8]>,
// ...
}
struct CowFoo02<'a, X>
where
[X]: ToOwned<Owned = Vec<X>>,
{
values: Cow<'a, [X]>,
}
impl<'a, X: Clone + 'a> CowFoo02<'a, X>
where
[X]: ToOwned<Owned = Vec<X>>,
{
fn new(v: Cow<'a, [X]>) -> Self {
CowFoo02 { values: v }
}
}
fn main() {
let a = Foo {
field1: "test_str",
field2: "test_str2",
field3: "test_str3".as_bytes(),
};
let a_owned = OwnedFoo {
field1: "test_String".to_string(),
field2: "test_String".to_string(),
field3: "Vec_u8".into(),
};
let a_cow_owned = CowFoo {
field1: "test_cow1".into(),
field2: "test_cow2".into(),
// field3: "test_cow03".into(),
// "test_cow3".into(),
};
dbg!(a);
dbg!(a_owned);
dbg!(a_cow_owned);
println!("~~~~~~~~~~~");
println!();
// Creates a container from borrowed values of a slice
let readonly = [1, 2];
let borrowed = CowFoo02::new((&readonly[..]).into());
match borrowed {
CowFoo02 {
values: Cow::Borrowed(b),
} => println!("borrowed {b:?}"),
_ => panic!("expect borrowed value"),
}
let mut clone_on_write = borrowed;
// Mutates the data from slice into owned vec and pushes a new value on top
clone_on_write.values.to_mut().push(3);
println!("clone_on_write = {:?}", clone_on_write.values);
// The data was mutated. Let's check it out.
match clone_on_write {
CowFoo02 {
values: Cow::Owned(_),
} => println!("clone_on_write contains owned data"),
_ => panic!("expect owned data"),
}
}
[src/main.rs:59:5] a = Foo {
field1: "test_str",
field2: "test_str2",
field3: [
116,
101,
115,
116,
95,
115,
116,
114,
51,
],
}
[src/main.rs:60:5] a_owned = OwnedFoo {
field1: "test_String",
field2: "test_String",
field3: [
86,
101,
99,
95,
117,
56,
],
}
[src/main.rs:61:5] a_cow_owned = CowFoo {
field1: "test_cow1",
field2: "test_cow2",
}
~~~~~~~~~~~
borrowed [1, 2]
clone_on_write = [1, 2, 3]
clone_on_write contains owned data
Upvotes: -1
Reputation: 299999
Building blocks:
Cow::into_owned
will return the owned version.'static
is the lifetime of the programTherefore we can write a utility function:
use std::borrow::Cow;
fn into_owned<'a, B>(c: Cow<'a, B>) -> Cow<'static, B>
where B: 'a + ToOwned + ?Sized
{
Cow::Owned(c.into_owned())
}
You can expand this to Foo<'a>
becoming Foo<'static>
by simply applying the transformation on all fields.
Upvotes: 11