Lifetime in Cow and deref

I have the following code:

use std::borrow::Cow;
use std::ops::Range;

pub struct F {
    s: String,
}

impl F {
    pub fn get_content(&self) -> Cow<str> {
        Cow::Borrowed(&self.s)
    }

    pub fn get_span(&self, span: Range<usize>) -> Option<Cow<str>> {
        self.get_content().get(span).map(|s| Cow::Borrowed(s))
    }
}

This, of course, raises a borrow error:

error[E0515]: cannot return value referencing temporary value
  --> <source>:14:9
   |
14 |         self.get_content().get(span).map(|s| Cow::Borrowed(s))
   |         ------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |         |
   |         returns a value referencing data owned by the current function
   |         temporary value created here

error: aborting due to previous error

Even with explicit lifetimes it won't compile.
In my understanding this should be safe and possible, because get_content is ties to self, but get_span is tied to self as well, and calling &'a self should return a Cow<'a, str>, doesn't it?

Is there any way to achieve this in safe rust?

The problem is that get_content might return a Cow::Owned, in which case in get_span you're gonna return a non-owned Cow that references a value that will be freed at the end of the function.

1 Like

Concretely, the Deref impl for Cow ties the lifetime of the returned reference to that of the &Cow itself (that's the only possible way due to the signature of Deref::deref()). I.e., for Cow<'a, str>, its deref impl returns &'self str, and not &'a str.

One easy fix is

pub fn get_span(&self, span: Range<usize>) -> Option<Cow<str>> {
    self.s.get(span).map(Cow::Borrowed)
}
1 Like

Of course! Thank you for clearing it up.
I haven't thought about the possibility of being an Owned value, but of course.

Today it hit me, you can of course use pattern matching!

pub fn get_span(&self, span: Range<usize>) -> Option<Cow<str>> {
    Some(match self.get_content() {
        Cow::Borrowed(b) => Cow::Borrowed(b.get(span)?),
        Cow::Owned(o) => Cow::Owned(o.get(span)?.to_string()),
    })
}

This should be pretty effiencent and zero copy if get_content is actually borrowed