Rust Subtyping
Subtyping lets one type stand in for another type. A longer lifetime is a subtype of a shorter lifetime.
A longer promise fills a shorter slot
hello is promised for the whole program. Storing it in a variable that only asks for a shorter promise is fine — the promise shrinks to fit:
let hello: &'static str = "hello";
let shorter: &str = hello;
println!("shorter = {}", shorter);Two lifetimes meet in one call
show_both takes two references under one shared lifetime. The literal brings the longest lifetime there is, the local String the shortest — Rust shrinks the longer one to match:
fn show_both(a: &str, b: &str) {
println!("a = {} b = {}", a, b);
}
let motto: &'static str = "stay";
let local = String::from("go");
show_both(motto, &local);The shrink passes through a Box
A Box holding a &'static str can be handed over as a Box holding a shorter &str. Forgetting the longer promise is safe: the value itself has not changed, only what is promised about it:
let boxed: Box<&'static str> = Box::new("kept for good");
let shorter: Box<&str> = boxed;
println!("boxed = {}", shorter);Stretching a short promise is refused
The other direction is the dangerous one. needs_static demands text that lives for the whole program, and a local String cannot promise that:
fn needs_static(s: &'static str) { println!("{}", s); }
let local = String::from("gone soon");
needs_static(&local); // error E0597: `local` does not live long enoughExample
fn announce(a: &str, b: &str) {
println!("{} and {}", a, b);
}
fn motto() -> &'static str {
"borrowed for good"
}
fn main() {
let local = String::from("gone soon");
announce(motto(), &local);
let calmer: &str = motto();
println!("calmer = {}", calmer);
println!("motto again = {}", motto());
}Key points
- Subtyping: one type may be used in place of another.
- It must meet every promise the other makes.
- The swap is silent, with no cast and no copy.
- 'static is a subtype of every lifetime.
- A longer stretch of validity contains every shorter one.
- It is written 'long <: 'short.
- It is read "'long is a subtype of 'short".
- A &'static str can stand in for any &'a str — the reference simply shrinks to the shorter lifetime
- The shrink is one-way.
- A short lifetime can never be stretched into a longer one.
- Code that tries fails with error E0597.
- Use this idea to read errors.
- When Rust says a value "does not live long enough", it found a mismatch.
- Only a shrink was allowed in that place.
- A stretch was attempted instead.
In this example, motto hands back a &'static str: text that lives for the whole program. announce expects two references under one ordinary lifetime. So the 'static promise shrinks. It sits beside the borrow of local, a String that dies at the end of main. The first line prints borrowed for good and gone soon. Below, calmer asks for a plain &str and receives the same 'static text, shrunk again. Nothing was copied and nothing was cast: the longer promise simply stood in for a shorter one, three times.
Note
Outside lifetimes, Rust has almost no subtyping.
Two different struct types are never subtypes of each other — there is no inheritance in Rust.
Try it Yourself
Edit the code, then press Run.
Running is simulated · Rust Playground
Exercise
Why may a &'static str be passed where a &str with a shorter lifetime is expected?