Why Cow<'a, str> lifetime cannot borrowed long enough?

I have a function look like this one:

use std::borrow::Cow;

fn foo<'a>(s: Cow<'a, str>) -> Option<Cow<'a, str>> {
    let trimed: &'a str = s.trim(); // borrowed value does not live long enough
    
    todo!()
}

Playground

Why I can't borrow the value as long as s have? Which step I violate the rule?
Thanks for help.

The problem you're hitting specifically here is that you're getting an &str via Deref, which constrains the lifetime of the reference it provides to the lifetime of the input reference.

There's a deeper problem though, which is that a Cow doesn't necessarily hold data with the lifetime parameter it's given. It can also contain owned data, and in that case you actually can't get an &'a str out of the Cow because the lifetime of the owned String is tied directly to the Cow and not 'a

2 Likes

this is a tricky situation, since you take Cow<'a, str> by value, it indeed might not live long enough. Cow<'a, T> doesn't deref to &'a T, but some other lifetime &'b T where 'b is the lifetime of the Cow itself. consider the situation:

let s = Cow::Owned<String::new()>;
let trimmed: Cow<'???, str> = foo(s).unwrap();
3 Likes

Here's a playground exploring what happens when you destructure the Cow.

1 Like

Thanks, this is an interesting case which stuck me a while. And now I know the primary reason I fail in here is because the difference between 'b and 'a in &'b Cow<'a, str>.

I got the 'b because the signature:

fn deref(self: &Self) -> &Self

... which should be thinking as:

fn deref<'a, 'b>(s: &'b Cow<'a, str>) -> &'b str

And then I think that maybe the problem of Deref are too strict, but after a while I believe @semicoleon is right:

So these is no chance to reuse the modified String in Cow if i don't want to change the foo's signature. (I mean, will, change foo to fn foo<'a, 'b: 'a>(&'b Cow<'a, str>) -> Cow<'b, str> :exploding_head:. Which may worthing to explore, but not needed in in my current use case )

And thanks for @quinedot source code, I final understand I don't need to borrow the s as long as 'a at all.

Here is what I final implementation (basically, I try to reuse logic in both TryFrom<&str> and TryFrom<String> without unnecessary allocation):

use std::borrow::Cow;

struct UserId(String);

impl UserId {
    /// Get normalized string format and validate it.
    fn normalized_str<'a, T: Into<Cow<'a, str>>>(s: T) -> Result<Cow<'a, str>, &'static str> {
        fn inner<'a>(s: Cow<'a, str>) -> Result<Cow<'a, str>, &'static str> {
            let trimmed: &str = s.trim();
    
            // validate
            if trimmed.is_empty() {
                return Err("bad format");
            }
    
            // try to reuse string buffer
            if trimmed == s {
                Ok(s)
            } else {
                Ok(trimmed.to_string().into())
            }
        }
        
        inner(s.into())
    }
}

impl TryFrom<&str> for UserId {
    type Error = &'static str;

    fn try_from(s: &str) -> Result<Self, Self::Error> {
        Self::normalized_str(s).map(|norm_str| Self(norm_str.into_owned()))
    }
}

impl TryFrom<String> for UserId {
    type Error = &'static str;

    fn try_from(s: String) -> Result<Self, Self::Error> {
        Self::normalized_str(s).map(|norm_str| Self(norm_str.into_owned()))
    }
}

Playground


And here is fn normalized_str<'a, 'b: 'a>(&'b Cow<'a, str>) -> Cow<'b, str> version (Edit: this version not truely useful, see @quinedot 's reply):

Playground

Anyway, thanks you all :smile:

1 Like

That's effectively one of

// If you never need ownership for some transformation
fn foo(&str) -> &str
// If you sometimes need ownership for some transformation
fn foo(&str) -> Cow<'_, str>
// If you always need ownership
fn foo(&str) -> String

For example.

2 Likes

Yay :sweat_smile:, after rethink a few minutes, I agree with you, fn foo<'a, 'b: 'a>(&'b Cow<'a, str>) -> Cow<'b, str> are not turely useful.

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.