Creating owned Cows

I hit this problem a few years ago and was able to workaround it (as shown below) but was never satisfied with the approach. I'd like to know if this can be achieved without a custom ToStatic trait?

The general idea is a tree of nested structs which typically borrow data but with the ability to ToOwned the entire tree when required. Minimal example below, hopefully I haven't over simplified it.

Given:

#[derive(Debug)]
pub struct Foo<'a> {
    name: Cow<'a, str>,
}

// We need a `T: 'static` here
fn bar<T: Debug + 'static>(foo: T) {
    dbg!(foo);
}

How can we create a Foo which we can move into bar()?

fn main() {
    let name = String::from("inner");
    let foo = Foo { name: Cow::from(&name) };
    // TODO invoke bar(..)
}

Attempting to use ToOwned:

impl ToOwned for Foo<'static> {
    type Owned = Foo<'static>;

    fn to_owned(&self) -> Self::Owned {
        Foo { name: Cow::Owned(self.name.clone().into_owned()) }
    }
}

fn main() {
    let name = String::from("inner");
    let foo = Foo { name: Cow::from(&name) };
    bar(foo.to_owned());
}

Fails (Rust Playground) with:

error[E0597]: `name` does not live long enough
  --> src/main.rs:39:37
   |
39 |     let foo = Foo { name: Cow::from(&name) };
   |                           ----------^^^^^-
   |                           |         |
   |                           |         borrowed value does not live long enough
   |                           argument requires that `name` is borrowed for `'static`
40 |     bar(foo.to_owned());
41 | }
   | - `name` dropped here while still borrowed

However, it can be made to work (Rust Playground) if we use a custom trait where the associated type is Static: 'static and a blanket impl for Cow:

pub trait ToStatic {
    type Static: 'static;
    fn to_static(&self) -> Self::Static;
}

impl<T> ToStatic for Cow<'_, T>
    where
        T: 'static + ToOwned + ?Sized,
{
    type Static = Cow<'static, T>;

    fn to_static(&self) -> Self::Static {
        Cow::Owned(self.clone().into_owned())
    }
}

impl ToStatic for Foo<'_> {
    type Static = Foo<'static>;

    fn to_static(&self) -> Self::Static {
        Foo { name: self.name.to_static() }
    }
}

fn main() {
    let name = String::from("inner");
    let foo = Foo { name: Cow::from(&name) };
    bar(foo.to_static());
}

Can this be achieved with ToOwned? Can we express the : 'static bound when declaring the the Owned = Foo<'static> associated type in the ToOwned impl?

The problem is that you don't want to implement ToOwned for Foo<'static>. You want to implement it for any and all Foo<'a>. However, that doesn't work either:

error: incompatible lifetime on type
   --> src/main.rs:15:5
    |
15  |     type Owned = Foo<'static>;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
note: because this has an unmet lifetime requirement
note: the lifetime `'a` as defined here...
   --> src/main.rs:14:6
    |
14  | impl<'a> ToOwned for Foo<'a> {
    |      ^^
note: ...does not necessarily outlive the static lifetime introduced by the compatible `impl`

for which, I suspect, the reason is that the Owned associated type has a Borrow<Self> bound. So I don't think this can be done via ToOwned.

2 Likes

Moreover, you can't implement

impl<'a> Borrow<Foo<'a>> for Foo<'static>

due to overlapping implementations. You can break the two cases into separate types:

#[derive(Debug)]
pub struct StaticFoo {
    foo: Foo<'static>,
}

And then -- assuming Foo is covariant in your use case like it is in this example case -- you can

impl<'a> Borrow<Foo<'a>> for StaticFoo {
    fn borrow(&self) -> &Foo<'a> {
        &self.foo
    }
}

At which point you will be able to

impl ToOwned for Foo<'_> {
    type Owned = StaticFoo;
    // ...
}

Playground.

Aside from the ease of going from StaticFoo back to Foo<'arbitrary>, I'm not sure how much benefit this has over StaticFoo just containing the owned variants.

2 Likes

Just write an inherent method:

impl<'a> Foo<'a> {
    // or whatever name you prefer
    fn into_static(self) -> Foo<'static> {
        Foo { name: Cow::Owned(self.name.into_owned()) }
    }
}

The reason to implement a trait (other than to more clearly express the intent of your code) is to opt in to the generic functionality that's written against that trait. If you don't need the generic functionality, inherent impls are just as good, or even better, since they're available wherever the type is and appear more prominently in rustdocs.

6 Likes

Indeed, that is quite right.

This is clever. I generalised your approach slightly by introducing a struct Static<T: 'static>(T) (Rust Playground) but I don't think we can avoid having the From and Borrow impls for each concrete Foo for the reason you noted about overlapping impls. One other very minor downside is that a more realistic receiving function (which expects to operate on a Foo rather than any generic Debug + 'static as in my example) would need to take an Into<Foo>.

This is an excellent point, it is very easy to fall into the trap of adding unnecessary abstraction and generalisation, guilty as charged.

In this specific case I always operate on a concrete Foo (or a Bar, Baz, etc) and never on an arbitrary : 'static thing and so the abstraction does not add much value here. Having said that, using a custom trait for this doesn't cost anything and I'd argue (subjective opinion) slightly simplifies the code at the call site for more complex structures.

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.