Vec<&str> referencing Vec<String> in the same struct

I'm gluing 2 libraries together and one is providing a Vec<String> and another one is accepting Vec<&str>. I thought the simplest way to make those Strings live long enough is to keep the original vector together with the vector of string slices.

(How) can I express that in Bar, the vector of lines is referencing Bar::foo field and not the input parameter which gets moved?

struct Foo {
    lines: Vec<String>,
}

struct Bar<'a> {
    lines: Vec<&'a str>,
    foo: Foo,
}

impl<'a> Bar<'a> {
    pub fn new(foo: Foo) -> Self {
        let lines = foo.lines.iter().map(|x| x.as_str()).collect();

        Self { lines, foo }
    }
}

(Playground)

Bar would be a self-referential struct in this case, so you would invalidate lines if Bar::foo where to be moved in memory. You can't express this is safe Rust, because Rust always assumes a struct will remain valid if it is moved.

How are you using Bar?

One solution that might get you closer to what you want is to store the lines in Bar using Cow:

Playground link.

I'd just generate the Vec<&str> whenever you need it, provided it's only required for a short period of time (as ought to be the case).

Thank you everyone. In the meantime I've changed the code so that the input and output types are identical but I'm still interested in a solution for cases when changing the code is not an option.

I suspected so. Wasn't sure how e.g. drop() would work if that was allowed so it smelled like it might not be possible.

Cow would work, but I think then I could just as well clone Strings and let fn lines() construct the vector of string slice.

Difficult to explain in my contrived example. :slight_smile: Essentially Foo represents the output of one component and Bar is the configuration for another component. The component is created once every now and then and reused. The real world has other structures, Bar is just an approximation and doesn't represent real code.

The code is async so I needed to stash the vector of Strings somewhere.

Do you mean something like @skysch's example?

I think what @droundy is saying is that you can just store Strings. Cow works better if you really don't want to pay to copy the strings in memory, but at the cost of storing and branching on the enum variant for each access. So the optimal choice really depends on how and how often you're using the data. If you have fewer, larger strings, Cow starts to make more sense.

String would probably be the easiest thing to do, and you could always switch to Cow later if it ends up being a performance issue.

1 Like

Thanks, I think I see what you mean.

I'm not sure it'd be an option in my specific case. I need to construct Vec<&str> once and pass it to create a component which then keeps passed in string slices. The component then uses all those slices for the entire duration of its lifetime and that's why I thought I could own Strings for the duration of the component and let the component refer to those Strings.

The cost of Cow would be negligible, I'd be working with around 100 Cows per second, surrounded by heavy IO operations.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.