Is there syntax documentation for lifetimes?

I have a member of a struct (not a reference) and it contains (or will when I get the syntax sorted) a reference so needs lifetime annotations (according to the compiler)

struct A {
  b:B, // <-- What do I say here?
}

struct B<'a> {
   foo:&'a SomeThingElse,
}
impl<'a> B<'a> { // Not sure about this....: 
: 
}

I am very frustrated with life times.

I understand why they are needed, but the syntax eludes me each and every time. There is no documentation for them that is not full of prose. And when I refactor the code not to use references I cannot leave the life time annotations there if they are not used so I have to re learn them every time.

The following is not what I am looking for....
https://doc.rust-lang.org/nomicon/lifetimes.html

What I do need is a list of all the syntax where life times may be used.

Asking about lifetimes on this forum a very common answer is "just avoid them". I can. By copying and cloning, but that defeats the point of Rust

To use references in something, introduce a generic type, like you did in B. If this were a List in other languages, you'd say

public static class MySecondList<T> {
    public List<T> innerList;
}

So, similarly, you'd be generic over the lifetime:

struct B<'a> {
   foo: &'a SomethingElse,
}
struct A<'a> {
   b: B<'a>,
}
impl<'a> B<'a> {
    //My functions using 'a
}

As long as you're familiar with generic type, you should be good to go! Lifetimes are declared and used in essentially the same way as any generic type (With some differences, for example HRTB).

You can include them in inline and where constraints:

struct Foo<'a, 'b: 'a, T: 'a>(&'a &'b T) where T: 'b;

And in functions too!

fn foo<'a>(x: &'a str) -> &'a str { &x[..] }

Why that syntax? impl<'a> B<'a> Why not: impl<'a> B?

What is HRTB?

Very helpful. But is this documented any place? As I stated above current documentation is pages and pages of prose and no list of syntax options. b: B<'a>, was the key piece of text I needed.

struct S1<'a>{
  foo: & 'a  SomeOtherThing,
}

struct S2<'a> {
  s1: S1<'a>,
}

impl<'a> S2 <'a> {
...
}

fn do_to_s2(that:&'a S2) {
}

But then there are the cases of using them in enum?

enum {
  Input<&'a str>,
}

Declared types?

pub type BBox = Box<B<'a>>;  // Invalid.  Undeclared life time.

What is the scope of life time operators? How many can I introduce and where? How do life times in different scopes interact with each other?

Because you say

impl<T> Vec<T>

and not

impl<T> Vec

Say we have a local function pointer with an unnameable lifetime:

fn foo<'a>(&'a str) {}

let x: ??? = foo; //What is the type of this function pointer?

Hence, we have HRTB:

let x: for<'a> fn(&'a str) = foo;

We essentially say for any lifetime 'a, this function works with that.
https://doc.rust-lang.org/nomicon/hrtb.html?highlight=HRTB

Probably this is a good place, but just generally follow the lifetimes-are-generics rule.

enums are each a single type, meaning that the generic parameters of each are not individual to each variant, if not to the enum as a whole.
So:

enum Foo<'a> {
    Input(&'a str)
    Nope
}

Type aliases can also be generic, but be warned that constraints on them aren't applied.

type Foo<'a> = &'a Foo;
type Bar<T> = &'static T;
type BBox<'a> = Box<B<'a>>;

They stack like generic parameters, and are descriptive and not prescriptive, in that they describe something instead of being assigned. You can make the lifetime bigger by defining it in a bigger scope, for example defining it in a static will make it live forever, therefore being the biggest lifetime.

You can introduce as many as the compiler allows, you could have:

fn foo<
  'a1, 'a2, 'a3, 'a4,
  'b1, 'b2, 'b3, 'b4,
  'c1, 'c2, 'c3, 'c4,
  'd1, 'd2, 'd3, 'd4,
   //Etc.
>() {}

You cannot redeclare lifetime names, meaning you cannot shadow their names like you can local variables. Simply put, lifetimes in different scopes are either assigned (IE, you name the lifetime in a function call with turbofish), or are named differently (If an impl has a lifetime, and a function has a lifetime in that impl, they must be named differently, but the function can reuse the lifetime in the impl).

2 Likes