Lifetime ('a)

'a is a Lifetime. It is not a type, but a label that describes how long a reference is valid.
A lifetime in Rust is the duration during which a reference is valid
Lifetime is only associated with references, it ensures memory safety without a garbage collector.
Rust Borrow checker ensures references never outlive the data they point to, preventing dangling pointers
Lifetime annotations look like <'a>, 'a, etc. — they're just labels for the compiler

Does not compile Compile (With Lifetime Annotation)

fn longest (x:& str, y:& str) -> & str{
    if x.len() > y.len() { x } else { y }
}
fn main() {
    let string1 = String::from("Hi");
    {
        let string2 = String::from("Hello");
        let result = longest (string1.as_str(), string2.as_str());
        println! ("{result}");
    }
}

$ cargo build
3 | fn longest(x:& str, y:& str) -> & str 
    |              -----    -----    ^ expected named lifetime parameter
                        

Why compliation error?
The return type (&str) from function longest() is a borrowed value, but Rust doesn't know whether it's borrowed from x or y.
Compiler does not know that the returned reference will remain valid as long as the any inputs are valid.
Lifetime?
  Prefix every parameter with 'a. Apostrophe ('a), denotes reference has generic lifetime.

fn longest <'a> (x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() { x } else { y }
}
                        

How this worked?
This says: "Both x and y live at least as long as returned lifetime 'a"
The returned reference will live at least as long as the shortest of the 2 input references

Lifetime types

1. Implicit (Elided) Lifetimes

Most lifetimes are automatically inferred by the compiler. You don't write them — they're "elided."


fn print_string(s: &str) {
    println!("{}", s);
}

let text = "Hello";
print_string(text);  // No lifetime annotation needed!

// The compiler infers that the return type (nothing here) doesn't 
// depend on the input lifetime. When a function only takes references
// but doesn't return them, lifetimes are elided.
      

2. Explicit (Named) Lifetimes

We write these when the compiler can't infer which lifetime a reference ties to — typically when returning a reference from multiple input references. Example above

3. 'static Lifetime

This is a special lifetime that means the reference lives for the entire duration of the program — typically for string literals or data stored in the binary.


fn GetName() -> &'static str {
    "This is a David"  // Lives forever!
}

let s = GetName();
println!("{}", s);  // Always safe
      

Lifetime in struct

Why we need lifetime in struct? if we want to store reference in struct, ie struct does not hold Owned types.

Will not compile Compiles (With Lifetime)

struct A {
    x:&str
}
fn main() {
    let m = String::from("test");
    let o = A {
        x : &m,
    };
    println!("{}", o.x);
}
$ cargo run
missing lifetime specifier
-- src/main.rs:6:7
6 |     x:&str
  |       ^ expected named lifetime parameter
                        

struct A <'a > {
    x : &'a str,
}
fn main() {
    let m = String::from("test");
    let o = A {
        x : &m,
    };
    println!("{}", o.x);
}
$ cargo run
test