What is the difference between 'a and 'static?

'a and 'static ?

'a is referring to a lifetime parameter, introduced somewhere. For example a function

fn return_one_of<'a>(x: &'a i32, y: &'a i32) -> &'a i32 {
    if rand::random() { x } else { y }
}

The lifetime argument 'a is introduced in the <> brackets after the function namesreturn_one_of, and then used in the function signature to the right.

The name 'a here is an arbitrarily chosen variable name, you could also write

fn return_one_of<'foo_bar>(x: &'foo_bar i32, y: &'foo_bar i32) -> &'a i32 {
    if rand::random() { x } else { y }
}

with the same effect.

On the other hand 'static is a specific lifetime that the compiler defines that is always available and needs not be (in fact cannot be) introduced.

As for the difference in meaning? Well they can be the same. If I call return_one_of with two &'static i32 references, then 'a gets instantiated to be 'static in this case, so the function can be used as if at had the signature fn return_one_of(x: &'static i32, y: &'static i32) -> &'static i32 in this case, but its actual generic signature is more general, supporting other use-cases, too.

The 'static lifetime is a special lifetime that lasts longer than any other lifetime, staying valid for the entire duration of the program. It’s the only concrete lifetime that has its own actual name; any other, shorter, lifetime in Rust does not have a concrete name; the shorter lifetimes of things like references to local variables are essentially unnamed, and are created automatically by the compiler as needed; and functions generic over lifetimes can be called on such references, making the compiler instantiate the lifetime argument 'a with one those shorter lifetime of a local variable.

E.g.

let x = 42;
let y = 1337;
let z: &i32 = return_one_of(&x, &y);

is a function call, where inside of this call, the lifetime parameter 'a is instantiated with the (inherently nameless) lifetime of the two borrows to local variables x and y here. (How exactly the borrows of two things can be reasoned about using a single lifetime is a different question that I’m not answering here right now, to keep this answer shorter.)


One interesting quirk of lifetimes is that – while 'static is always the same lifetime, starting at some unspecified point and ending … essentially never … or “when the program ends”, a lifetime of a local variable can actually be lots of different lifetime at once. E.g. if called in a loop,

loop {    
    let x = 42;
    let y = 1337;
    let z: &i32 = return_one_of(&x, &y);
}

then each loop iteration will call return_one_of arguable with a new lifetime that only exists within that particular loop iteration, ending between the point where z gets assigned and the end of the loop body.

Also, borrow checking is arguably more complex than simple assignments of lifetimes to references and sections of code; it’s best to just develop an intuition as to what kind of code does or doesn’t get accepted, and lifetime variables such as 'a are mostly used for function signature, so it’s mainly just important to understand what they mean in function signatures. E.g. the signature of return_one_of above tells me that the returned reference has the same lifetime as the two argument references, which means that the returned reference will keep both of the original borrows alive. Or that the returned references will be considered to borrow from the same thing as both input references. Thus code like

let mut x = 42;
let y = 1337;
let z: &i32 = return_one_of(&x, &y);
x += 1; // <- modify `x` here, which requires the `&x` borrow from the previous line to have ended
println!("{}", z); // keep `z` alive until here, by using it here
error[E0506]: cannot assign to `x` because it is borrowed
 --> src/main.rs:5:1
  |
4 | let z: &i32 = return_one_of(&x, &y);
  |                             -- borrow of `x` occurs here
5 | x += 1;
  | ^^^^^^ assignment to borrowed `x` occurs here
6 | println!("{}", z);
  |                - borrow later used here

As you can see, the compiler did (perhaps unfortunately) not even bother to tell us the connection between x and z, but the error only makes sense once you realize that the function signature of return_one_of had the effect that the property “borrows from x” transfers from the &x reference to the reference stored in z; or put differently, that using z and keeping z alive will also keep the &x argument alive.

3 Likes

I should also mention the relevant chapter in the book…

Validating References with Lifetimes - The Rust Programming Language

An example:

fn foo<'a>(input: &'a str) -> &'a str {
    if input == "" {
        "empty" // this is `&'static str`, but can be coerced to `&'a str`
    } else {
        input // this is `&'a str` because `input` is of type `&'a str`
    }
}

fn bar<'a>(input: &'a str) -> &'static str { // if we used &'a str as return type, `baz` would not compile
    if input == "" {
        "empty" // this is `&'static str`
    } else {
        "non-empty" // this is `&'static str`
    }
}

fn baz(b: bool) -> &'static str {
    let mut s = String::new();
    if b {
        s.push('X');
    }
    bar(&s)
}

fn main() {
    assert_eq!(foo(""), "empty");
    assert_eq!(bar(""), "empty");
    assert_eq!(foo("Thomas"), "Thomas");
    assert_eq!(bar("Thomas"), "non-empty");
    assert_eq!(baz(false), "empty");
    assert_eq!(baz(true), "non-empty");
}

(Playground)

Or the very short (and inaccurate) rule of thumb: 'static is as long as it needs to be, 'a is as short as it needs to be.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.