Difference between `&self` and `&'a self` in `impl<'a>`

The following code can't compile. If replace ss.bar() to ss.foo() then it works as intended.
foo and bar only differs in receiver signature: &self and &'a self, which I initially supposed to be the same, as they are both in block of impl<'a> StrangeSplitter<'a>.

Here comes my suppose:

  1. My guess is the explicit version of ss.foo() would be the following.
    fn foo<'b>(&'b self) -> &'a [u8] where 'a:'b
  1. The "lifetime" of ss in fn split is also required to be shorter than (fully spanned by) lifetime of both ss.raw and return value of ss.foo().

Are 1. and 2. right?

The origin goal was to create a struct that accepts AsRef<[u8]>+'a and provides several slices valid even after the splitter gets dropped. In another word, a struct that owns, process and return references with lifetime beyond (lives longer than) the struct itself.

Any suggestion on better practice would be appreciated.

Test code: (Playground link here)

struct StrangeSplitter<'a> {
    raw: &'a [u8],
}

impl<'a> StrangeSplitter<'a> {
    fn foo(&self) -> &'a [u8] {
        &self.raw[0..1]
    }
    fn bar(&'a self) -> &'a [u8] {
        &self.raw[0..1]
    }
}

fn split<'a>(raw: &'a [u8]) -> &'a [u8] {
    let ss: StrangeSplitter<'a> = StrangeSplitter { raw };
    // let a: &'a [u8] = ss.foo(); //This is okay
    let a: &'a [u8] = ss.bar();
    return a;
}

fn main() {
    let raw: Vec<u8> = (0..8).collect();
    let _ = split(&raw);
}

Compiler output:

error[E0597]: `ss` does not live long enough
  --> src/main.rs:17:23
   |
14 | fn split<'a>(raw: &'a [u8]) -> &'a [u8] {
   |          -- lifetime `'a` defined here
15 |     let ss: StrangeSplitter<'a> = StrangeSplitter { raw };
   |         -- binding `ss` declared here
16 |     // let a: &'a [u8] = ss.foo(); //This is okay
17 |     let a: &'a [u8] = ss.bar();
   |            --------   ^^ borrowed value does not live long enough
   |            |
   |            type annotation requires that `ss` is borrowed for `'a`
18 |     return a;
19 | }
   | - `ss` dropped here while still borrowed

1 is right. It also means that the return value of foo doesn't keep the StrangeSplitter itself borrowed, while with bar it does. &'a self is a yellow flag.

I'm not sure I understand what you meant by 2. You annotated it to be the same lifetime as raw. The source of the compiler error is that the return of bar borrows the local StrangeSplitter, and you can never return a borrow of a local.

With borrow-managing types, you generally want to take a &[mut] self lifetime unrelated to your managed borrow ('a), but return the managed lifetime. Lifetime elision can work against you for this use case.

Are you trying to hold on to the AsRef implementor too? If so you'll probably need to accept &'a T where T: ?Sized + AsRef<[u8]> instead, and hold on to that (or the slice reference you get out). (Otherwise there's a good chance you're trying to create a self-referencial struct.)

1 Like

This is a revelation. I didn't understand the compiler error well before.

I might be inexplicit on point 2. I suppose the lifetime span requirement would be: ('a<:'b means 'a is longer than 'b to be clear for me :slight_smile: )

raw(in main, the owned one) <: &raw(parameter raw in split) = a(ret val of split) <: ss

As for 'a in split, "lifetime" of ss is never longer beyond function end, and 'a in ss: StrangeSplitter<'a> has nothing to do with "lifetime" of ss, except for it('a) should be longer than "lifetime" of ss.

And yes, my real code is just as you said, raw: &'a T where T: ?Sized + AsRef<[u8]>. Seems I've reached the best practice with luck.

Thanks for your help!