Question
In Rust, I have seen types and annotations written with a leading apostrophe, such as:
'static
'r
'a
What does the apostrophe (') mean in these cases?
Are these related to references like &T? Are they a kind of generic parameter that applies specifically to references?
I am trying to understand what these identifiers represent and where they fit into Rust's type system.
Short Answer
By the end of this page, you will understand that names like 'a, 'r, and 'static in Rust are lifetimes. Lifetimes describe how long references are valid, helping Rust prevent dangling references at compile time. You will see how lifetime annotations appear in function signatures, structs, and reference types, and how 'static is a special lifetime with a distinct meaning.
Concept
In Rust, an identifier that starts with an apostrophe, such as 'a, usually represents a lifetime.
A lifetime is Rust's way of describing how long a reference is allowed to stay valid. Since Rust cares deeply about memory safety, it must ensure that references never outlive the data they point to.
For example, if a reference points to a local variable inside a function, that reference cannot be used after the variable goes out of scope. Rust checks this at compile time.
Why lifetimes exist
Rust allows you to use references instead of copying data everywhere. That is efficient, but it creates a risk:
- a reference might point to data that has already been dropped
- this would create a dangling reference
Rust prevents that by tracking lifetimes.
What the apostrophe means
The apostrophe is simply part of the syntax for naming a lifetime:
'a'r'input'static
These are not string literals, not characters, and not special modifiers on & by themselves. They are names used by the compiler to reason about borrowing.
Lifetimes and references
Lifetimes are most commonly seen with references because references are the values that depend on something else remaining alive.
For example:
fn print_name<'a>(name: &'a str) {
println!("{}", name);
}
Here:
<'a>declares a lifetime parameter named'a&'a strmeans: "a string slice reference that is valid for at least lifetime'a"
Lifetime parameters are similar to generic type parameters
This is a good intuition. Lifetimes are like generic parameters for borrowing duration.
Compare:
fn identity<T>(value: T) -> T {
value
}
with:
fn borrow_identity<'a>(value: &'a str) -> &'a str {
value
}
Tis a generic type parameter'ais a generic lifetime parameter
So yes: lifetime annotations are a kind of generic parameter, commonly used with references.
What 'static means
'static is a special lifetime. It means the referenced data can live for the entire duration of the program.
Example:
let message: &'static str = "hello";
String literals are stored in the program binary, so they live for the whole program.
Important note
Most Rust code does not require you to write lifetimes everywhere. Rust often infers them using lifetime elision rules. You usually write lifetimes only when the relationships between references need to be made explicit.
Mental Model
Think of a reference as a library card that lets you access a book without owning it.
The lifetime is the expiration date on that card.
&Tmeans you have access to the book'ameans how long that access card stays valid- Rust checks that your card does not expire before you try to use it
So if a function returns a reference, Rust wants to know: which original value does that reference depend on, and how long is it safe to use?
'static is like a card that never expires during the whole program run.
This model helps explain why lifetimes are attached to borrowed data rather than owned values. Owned values manage their own memory. References borrow someone else's memory, so Rust must track validity.
Syntax and Examples
Core syntax
Lifetime parameter declaration
fn example<'a>(value: &'a str) -> &'a str {
value
}
Reference with an explicit lifetime
let s: &'static str = "hello";
Struct holding a reference
struct UserName<'a> {
name: &'a str,
}
A struct that stores a reference usually needs a lifetime parameter, because the struct cannot outlive the data it borrows.
Beginner-friendly example
fn first_word<'a>(text: &'a str) -> &'a str {
match text.split_whitespace().next() {
Some(word) => word,
None => "",
}
}
fn main() {
let sentence = String::from("hello rust world");
let word = first_word(&sentence);
println!("{}", word);
}
What this means
sentenceowns the string data&sentenceborrows it as a string slicefirst_wordreturns a reference into the same string data- the returned reference cannot outlive
sentence
The lifetime 'a tells Rust that the input reference and output reference are connected.
Special case: 'static
fn get_message() -> &'static str {
"ready"
}
This works because string literals have the 'static lifetime.
Step by Step Execution
Consider this function:
fn longer<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
fn main() {
let s1 = String::from("cat");
let s2 = String::from("elephant");
let result = longer(&s1, &s2);
println!("{}", result);
}
Step by step
s1is created and owns the string"cat".s2is created and owns the string"elephant".&s1creates a reference to the contents ofs1.&s2creates a reference to the contents ofs2.- The function
longeraccepts two references that share the lifetime parameter'a. - Rust interprets this as: the returned reference must be valid for the lifetime shared by both inputs.
- Inside the function, Rust compares lengths.
- It returns either
xory, both of which are references tied to'a. - In
main,resultis safe to use because boths1ands2are still alive.
Why the shared lifetime matters
The function returns one of the two input references. Rust needs a rule describing how long that output is valid. The annotation says: the output is valid no longer than the borrowed inputs allow.
What Rust is protecting you from
Rust prevents code like this:
fn bad_reference<'a>() -> &'a str {
let s = String::from("temporary");
&s
}
This is invalid because s is dropped at the end of the function, so returning a reference to it would be unsafe.
Real World Use Cases
Lifetimes show up whenever code borrows data instead of owning it.
Common real uses
- String processing: returning slices like
&strinstead of allocating newStringvalues - Parsers: storing references into the original input buffer
- Configuration views: borrowing parts of larger config data
- Structs with borrowed fields: avoiding unnecessary cloning
- APIs that avoid allocation: returning borrowed data for performance
Example: parsing without copying
struct Header<'a> {
key: &'a str,
value: &'a str,
}
This lets a parser read text and store references to pieces of the original input, instead of creating new owned strings for every field.
Example: efficient helper functions
fn trim_and_keep<'a>(s: &'a str) -> &'a str {
s.trim()
}
This returns a borrowed slice of the original string instead of allocating a new one.
Real Codebase Usage
In real Rust projects, developers often use lifetimes indirectly through references, slices, and iterators. Explicit lifetime annotations usually appear when the relationship between borrows must be spelled out.
Common patterns
Guarding borrowed inputs
fn non_empty<'a>(s: &'a str) -> Option<&'a str> {
if s.is_empty() {
None
} else {
Some(s)
}
}
This pattern keeps the returned borrow tied to the input.
Borrowed views instead of owned copies
struct RequestView<'a> {
path: &'a str,
method: &'a str,
}
This is common in parsers, web frameworks, and protocol handling code.
Returning slices from collections
fn first_item<'a>(items: &'a [i32]) -> Option<&'a i32> {
items.first()
}
Validation without cloning
Developers often validate borrowed input like &str or &[u8] rather than allocating new data.
Error handling with borrowed context
Some code uses borrowed references in error messages or diagnostic structures, though owned types are also common when lifetimes become too complex.
Practical advice
In production Rust code:
- prefer borrowing when it avoids unnecessary copying
- use owned types when lifetime relationships become too difficult
- let the compiler infer lifetimes when possible
- add explicit lifetimes only when needed to express relationships clearly
Common Mistakes
1. Thinking 'a creates data
It does not create or store data. It only describes how long a reference is valid.
Broken mental model:
// 'a is not a value and does not allocate anything
2. Confusing 'static with "always better"
'static is not a general replacement for other lifetimes.
Broken example:
fn wrong() -> &'static str {
let s = String::from("hello");
&s
}
This fails because a local variable does not live for the whole program.
3. Returning a reference to local data
fn broken<'a>() -> &'a str {
let text = String::from("oops");
&text
}
Why it fails:
textis dropped when the function ends- the returned reference would point to invalid memory
How to fix it:
- return an owned
String - or borrow from an input parameter instead
4. Forgetting that structs with references need lifetimes
Broken code:
struct Person {
name: &str,
}
Fixed:
struct Person<'a> {
name: &'a str,
}
5. Adding lifetimes everywhere unnecessarily
Many lifetimes are inferred.
fn print_name(name: &str) {
println!("{}", name);
}
No explicit annotation is needed here.
6. Assuming lifetimes change runtime behavior
Lifetimes are mostly a compile-time checking tool. They do not usually add runtime cost.
Comparisons
Lifetimes vs related Rust concepts
| Concept | What it describes | Example | Owns data? |
|---|---|---|---|
| Lifetime parameter | How long a borrow is valid | 'a | No |
| Reference | Borrowed access to data | &str | No |
| Mutable reference | Exclusive borrowed access | &mut String | No |
| Generic type parameter | A placeholder for a type | T | Not by itself |
| Owned value | Data that manages its own memory | String | Yes |
'a vs 'static
| Syntax | Meaning |
|---|---|
'a | A named lifetime parameter chosen for a function, struct, or impl |
'static | A special lifetime meaning the data can live for the entire program |
Borrowed vs owned strings
| Type | Meaning | Typical use |
|---|---|---|
String | Owned string data | Store and modify text |
&str | Borrowed string slice | Read existing text without copying |
&'static str | Borrowed string slice valid for whole program | String literals, constant messages |
When explicit lifetimes are needed
| Situation | Usually needed? |
|---|---|
| Simple function taking one reference | Often no |
| Function returning one of multiple input references | Yes |
| Struct storing references | Yes |
| String literal types | Sometimes visible as 'static |
Cheat Sheet
Quick reference
Lifetime syntax
'a
'input
'ctx
'static
Reference with lifetime
&'a T
&'a str
&'a mut T
Function with a lifetime parameter
fn example<'a>(x: &'a str) -> &'a str {
x
}
Struct with borrowed data
struct Item<'a> {
name: &'a str,
}
Meaning of 'static
let msg: &'static str = "hello";
- valid for the whole program
- common with string literals
Key rules
- lifetimes usually describe references, not owned values
- lifetime parameters are like generic parameters for borrows
- a returned reference cannot outlive the data it points to
- structs containing references usually need lifetime parameters
- Rust often infers lifetimes, so explicit annotations are not always necessary
Red flags
- returning a reference to a local variable
- forcing
'staticwhere data is not actually static - storing references in structs without declaring a lifetime
Good default approach
- use
&stror&[T]for borrowing - use
StringorVec<T>when ownership is simpler - add explicit lifetimes only when the compiler needs help
FAQ
What does the apostrophe mean in Rust types?
It usually introduces a lifetime, such as 'a or 'static. Lifetimes describe how long a reference is valid.
Is 'a part of a reference type?
It is not the reference itself, but it is often attached to references, like &'a str, to describe the borrow's lifetime.
Are lifetimes like generics in Rust?
Yes. A lifetime parameter like 'a is similar to a type parameter like T, but it represents duration of validity rather than a type.
What is the difference between 'a and 'static?
'a is a named lifetime parameter chosen for a specific context. 'static is a special lifetime meaning the data can live for the entire program.
Why do some functions not show lifetimes at all?
Rust can infer many lifetimes automatically using lifetime elision rules.
Do lifetimes affect runtime performance?
Usually no. Lifetimes are mainly checked at compile time.
Why do structs with references need lifetimes?
Because Rust must know how long the referenced data must stay alive for the struct to remain valid.
Mini Project
Description
Build a small Rust program that returns borrowed pieces of text without allocating new strings. This demonstrates why lifetimes exist and how they let functions safely return references tied to input data.
Goal
Create a function that finds the longer of two string slices and returns a safe borrowed reference to it.
Requirements
- Write a function that accepts two
&strvalues. - Return the longer string slice without creating a new
String. - Use an explicit lifetime annotation in the function signature.
- Test the function with two different input strings.
Keep learning
Related questions
Absolute Values in Rust: Using the abs() Method
Learn how to calculate absolute values in modern Rust with abs(), handle signed integer overflow, and choose safer alternatives.
Accepting Iterators in Rust Functions with impl Iterator
Learn how to write Rust functions that accept HashMap iterators using generic bounds or impl Iterator, avoiding trait object and lifetime errors.
Access Enum Variant Values in Rust with Pattern Matching
Learn how to extract and print values stored in Rust enum variants using match and if let pattern matching, with practical examples.