2

On page 295 of Programming Rust you can find the following:

Fortunately, the standard library includes the blanket implementation:

impl<'a, T, U> AsRef<U> for &'a T
where
    T: AsRef<U>,
    T: ?Sized,
    U: ?Sized,
{
    fn as_ref(&self) -> &U {
        (*self).as_ref()
    }
}

I'm confused at the use of &'a there. What is the context of that? It's not being used in an argument of as_ref nor tied to the output of &U. I don't think I fully understand lifetimes when used in this context.

I looked this up because I still didn't understand it and the answers still weren't clicking (making sense). I found convert.rs. This doesn't seem to have any lifetimes anywhere, but it implements the AsRef trait. So why does the book have this, and not the actual code in Rust? Where can I find the "blanket implementation" mentioned in the book?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Evan Carroll
  • 78,363
  • 46
  • 261
  • 468
  • *and not the actual code in Rust* — are you aware that Rust releases new versions every 6 weeks? Presumably the book does not, so *what version of Rust are they citing*? – Shepmaster Sep 25 '19 at 02:23
  • @Shepmaster step back, I'm new. Why would there be a difference? My question is simply that I don't understand the lifetime use in the book, and going to see the code I don't see it there (which is what I would expect). Why did it change (if it did). – Evan Carroll Sep 25 '19 at 02:37
  • *Why did it change (if it did)* — that's covered by [an existing answer](https://stackoverflow.com/a/58030681/155423): *There used to be a time when there was no possibility for lifetime elision in implementation statements. This changed in version 1.31 of the compiler* – Shepmaster Sep 25 '19 at 02:39
  • I do think you come off with questions that are obvious. **Of course** I realize books can be out of date. I just wanted to see the comparison between the *blanket implementation* the book has and the new one, and you provided it at the same time I had asked this question, so I think I communicated my confusion. I just didn't have the Rust expertise to know that the code in your answer was the replacement. (Mostly because when I mentally elided `&'a T` I dropped `&'a` and I went looking for `T` instead of `&T` and then was confused because I thought it only worked on functions and not impls. – Evan Carroll Sep 25 '19 at 02:54
  • 1
    @Shepmaster anyway, thanks again for the answer ;) – Evan Carroll Sep 25 '19 at 02:54
  • 1
    I don't see what question I asked was obvious. I didn't ask if you knew the book could be out of date. I asked if you knew that (1) Rust releases frequently (C and C++ releases are every 3-5 years, for comparison, so it's more likely that a book would correspond to the most recent release) and (2) what version the book uses. The first isn't obvious and the second isn't something that is general knowledge (and I don't own that book). – Shepmaster Sep 25 '19 at 02:58
  • Fair enough, perhaps I read into it wrong. – Evan Carroll Sep 25 '19 at 03:02

2 Answers2

5

It's not being used in an argument of as_ref

It most certainly is. The function uses a shorthand notation, which can be expanded:

fn as_ref(&self) // becomes
fn as_ref(self: &Self) // becomes
fn as_ref(self: &&'a T)

nor tied to the output of &U

Correct.

So why does the book have this, and not the actual code in Rust?

Rust releases new stable versions every 6 weeks. Presumably the book does not, so it is likely that they are using an older version of Rust. Hopefully the book tells you the version they developed with.

As E_net4 already stated, the requirement to specify the 'a in this case was removed in Rust 1.31, as documented in the edition guide.

The code you provide from the book matches that found in Rust 1.30:

impl<'a, T: ?Sized, U: ?Sized> AsRef<U> for &'a T where T: AsRef<U>
{
    fn as_ref(&self) -> &U {
        <T as AsRef<U>>::as_ref(*self)
    }
}

The source code you looked at corresponds to Rust 1.37:

impl<T: ?Sized, U: ?Sized> AsRef<U> for &T where T: AsRef<U>
{
    fn as_ref(&self) -> &U {
        <T as AsRef<U>>::as_ref(*self)
    }
}

This is about 42 weeks of development time, plenty for the source code to have changed.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • I like this answer because this shows all aspects including the relevant changes to `convert.rs`. I also see that `&'a T` (and not `&'a &T`) is `&T`. Anyway, I think I'm closer to wrapping my head around this now. thanks – Evan Carroll Sep 25 '19 at 02:50
4

References are always generic to a lifetime. In practice, a &T is always a &'a T for some lifetime established by the compiler according to the given circumstances. This lifetime has to be specified in some way when implementing something for a reference type.

There used to be a time when there was no possibility for lifetime elision in implementation statements. This changed in version 1.31 of the compiler, but there was no need to change all existing, working code because of this. The code below works today, but not in version 1.30.0:

trait Foo {
    fn foo(&self) {}
}

impl<T: ?Sized> Foo for &T {} // error[E0106]: missing lifetime specifier

As such, the lifetime parameter 'a was made explicit in this case. The only way it relates from the lifetimes in &self and &U is that there will be covariance with 'a: since self = &'a T is bound to the lifetime 'a, it is also implicit that &self must not outlive the lifetime 'a.

E_net4
  • 27,810
  • 13
  • 101
  • 139