Simple error while implementing Borrow for my Wrapper type

hi folks!

struct Wrapper(String);

impl Borrow<str> for Wrapper {
    fn borrow(&self) -> &str {
        &*self.0.as_str()
    }
}

below why does one have to return a borrow with lifetime 'a (confused because we nowhere annotate this in the signature)? Is it because of the implicit bound: 'a: '1 that the compiler just wants us to return something that lives at least as long as 'a?

impl<'a> Borrow<&'a str> for Wrapper {
    //        &'1       &'1
    //        v         v
    fn borrow(&self) -> &&'a str {
        // error: method was supposed to return data with lifetime `'a`
        &self.0.as_str()
    }
}

you are trying to implement Borrow<&'a str> for Wrapper.

so the signature of the fuction is defined for you by the Borrow, adn you cannot change it.
it is

fn borrow(&self) -> &Borrowed

which is a shorthand for

fn borrow<'s>(&'s self) -> &'s Borrowed

so your particular signature is exactly

fn borrow<'s>(&'s self) -> &'s &'a str

now, String::as_str has for signature

fn as_str<'s>(&'s self) -> &'s str

so when you call self.0.as_str()
you are calling String::as_str(&self.0)
but self is &'s self, so &self.0 is at most &'s String,and so the output of String::as_str(&self.0) is again at most &'s str, or what the compiler calls &'1 str.
the output of your function is supposed to be &'s &'a str, but you have failed to produce any &'a str, because 'a could be much longer than 'r.
and ofc what you are trying to do is impossible , aside from always returning &"".
also when you write &self.0.as_str() you are returning a reference to a local variable (the &'1 str that as_str just produced), which is always impossible too.

At a higher level, the signature of the trait...

trait Borrow<T: ?Sized> {
    fn borrow(&self) -> &T
}

...implies that there is a T somewhere in *self -- in a field, behind another indirection or such (for the most part[1]). Wrapper(String) contains a str in this sense, but it does not contain a &'a str.

And beyond "containment", note that this attempt...

impl<'a> Borrow<&'a str> for Wrapper {

...means that Wrapper implements Borrow<&'static str>, which means if it compiled, a caller could do this.

fn example(wrapper: Wrapper) {
    let r: &&'static str = wrapper.borrow();
    let s: &'static str = *r;
    drop(wrapper);
    println!("{s}");
}

That's clearly unsound if the str data is owned by the Wrapper. So your &'a str implementation which tries to return the contents of the owned String must not be allowed,[2] no matter what the particular errors happen to be.

(The same demonstration can be made with any lifetime, not just 'static. But 'static is probably easier to understand.)

If the above doesn't make sense to you, try to articulate why and I'll try to explain more.


This is a question about a particular error. First, you did annotate the 'a in the signature. You need to return a &'1 &'a str. Instead as @Morgane55440 walked through in more detail, your return expression is a

&'fresh_borrow_of &'1 str

And you can't coerce &&'1 str to &&'a str unless '1: 'a, which is not the case here. That's the first error ("lifetime may not live long enough"). Additionally the &'1 str is a local temporary, and you can't return references to locals. That's the second error ("cannot return reference to temporary value").

So it's not because of 'a: '1, it's because (in part) there is no '1: 'a bound[3] (and because you can't return references to locals -- you contain no &str s to return a reference to).


  1. You could also return a static, return something leaked, use unsafe to convert to something compatible ("ref wrapping"), etc. ↩︎

  2. assuming safe code ↩︎

  3. and the shape of the trait doesn't allow one ↩︎