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!()
}
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
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();
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>. 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()))
}
}
// 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