3

Now that I made myself a little bit familiar with the basic concept of lifetimes in Rust I'm facing a different problem. I'm having a pretty hard time to wrap my head around the lifetime syntax.

Is there a general rule when to use which syntax for lifetime annotations in Rust? It seems to me there are so many variations on the syntax and I always have to poke around a lot to grab the right syntax when I want to add a lifetime somewhere.

I came across:

without lifetime                with lifetime

Vec<Route>                      Vec<Route<'a>>
&Route                          &'a  Route
impl Clone for Route            impl<'a> Clone for Route<'a>
||                              ||:'a

There are so many different variations on the syntax. Sometimes you go from Vec<Route> to <Vec<Route<'a>>, means you have to introduce angle brackets. Sometimes you go just from &Route to &'a Routeso you only add a 'a to your existing code. A different time you have to add :'a. I also don't get why we need to add <'a> to the impl keyword as well.

To be more clear, why not say lifetimes are always ^aor something so that the above list would boil down to:

without lifetime                with lifetime

Vec<Route>                      Vec<Route^a>
&Route                          &^a  Route (or Route^a ?)
impl Clone for Route            impl Clone for Route^a
||                              ||^a
Christoph
  • 26,519
  • 28
  • 95
  • 133

2 Answers2

3

Rust differentiates between lifetime declaration and use. Lifetimes are only used with references.

&'a Bar states that lifetime a is used by the reference to Bar.

<'a> declares a lifetime. It means that the preceding item, for example Foo<'a> or fn do_stuff<'a> contains one or more references that requires a lifetime annotation.

As far as I know, || is actually a reference to a closure object (closures are being redone at the moment).

Edit: for completeness, 'static lifetimes do not have to be declared.

A.B.
  • 15,364
  • 3
  • 61
  • 64
  • But I don't get why Rust needs to have so many different variations. Why not just strictly use `^lifetime` no matter if it's annotating a reference, a closure or a struct. It would just be `&^lifetime`, `||^lifetime` and `Route^lifetime` instead of `&'a`, `||:'a` and `Route<'a>`. This would make so much more sense to me. Not? – Christoph Jun 17 '14 at 21:01
  • @Christoph, you can have multiple lifetimes in a struct, e.g. `struct Foo<'a, 'b> { x: &'a Bar, y: &'b Baz }`; for types, lifetimes are just like other generics (except they are slightly optional). – huon Jun 17 '14 at 21:58
  • Yep, I did realize I can have multiple lifetimes. Still I find the syntax so confusing. For instances I first tried `||<'a>` then `||'a` until I realized it's `||:'a`. And why is it `&'a` but not `||'a`? @Corey Richardson left a note about *"Kind" bound* but I didn't quite get it yet. – Christoph Jun 17 '14 at 22:12
2

There are really only two usecases here: type parameters and references. To add type parameters to any type, you need to add angle brackets. Not every type has type parameters, and sometimes you can leave them anonymous and inferred. Every reference has a lifetime, though, and only rarely can it be left to be inferred or anonymous. So let's categorize your original examples by the positions the lifetime appears in:

  • Type Parameter: Vec<Route> vs Vec<Route<'a>>
  • Reference: &Route vs &'a Route
  • Type Parameter: impl Clone for Route vs impl<'a> Clone for Route<'a>
  • "Kind" Bound: || vs ||:'a

Note that, as a type parameter, it's rarely legal to leave off the lifetime (well, as dbaupp points out, just as valid as when you can leave it off a reference, but that comes up far more often in practice). As a "kind" bound, it's using the same syntax as bounds on a type parameter would. Using the same syntax for lifetimes and type parameters satisfies two things, one of which is more practical than the other:

  1. (More practical) specifying multiple lifetimes is natural, Route<'a, 'b>
  2. Combining lifetimes and type parameters isn't ugly. Route^a<T>?
ember arlynx
  • 3,129
  • 2
  • 20
  • 22
  • Can you explain what a "Kind" bound is? – A.B. Jun 17 '14 at 21:09
  • "Note that, as a type parameter, it's rarely legal to leave off the lifetime" it's exactly as legal to leave off the lifetime as with closures and references? – huon Jun 17 '14 at 21:30
  • Thank you for your answer. Can you describe a bit more what a *"Kind" bound* is? Please note that in my proposal I would still write `Route<^a T>` not `Route^a`. But honestly, I don't even argue about the current syntax with the angle brackets for **non-generic** types but then for consistency i would kind of expect `&<'a> Route` and `||<'a>` as well. – Christoph Jun 17 '14 at 22:16
  • I suppose those would also be valid, but I'd argue that those read wrong. You are not taking an instance of & substituting a lifetime parameter with `'a`, the lifetime is always there, you're just allowed to omit it often. – ember arlynx Jun 17 '14 at 22:35
  • A kind bound is similar to a trait bound, but for the built-in traits. I only hesitate to call them trait bounds here because `'a` and `'static` obviously aren't traits! It lets you restrict the closure in certain ways. You can say `||:Send`, for example, or `||:Send+'a`. I agree that the syntax isn't very consistent here, but closures are changing anyways. – ember arlynx Jun 17 '14 at 22:37