dnaq
dnaq

Reputation: 2244

Convenient way to transform struct with Cow-fields to owned

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 mmapped 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 Foos 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 Cows 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

Answers (2)

globalyoung7
globalyoung7

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"),
    }
}
  • Result
[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

Matthieu M.
Matthieu M.

Reputation: 299999

Building blocks:

  • Cow::into_owned will return the owned version.
  • 'static is the lifetime of the program

Therefore 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

Related Questions