diff --git a/src/unbounded-lifetimes.md b/src/unbounded-lifetimes.md index 03febd6..9ca2a2f 100644 --- a/src/unbounded-lifetimes.md +++ b/src/unbounded-lifetimes.md @@ -1,13 +1,13 @@ # Unbounded Lifetimes Unsafe code can often end up producing references or lifetimes out of thin air. -Such lifetimes come into the world as *unbounded*. The most common source of this -is dereferencing a raw pointer, which produces a reference with an unbounded lifetime. -Such a lifetime becomes as big as context demands. This is in fact more powerful -than simply becoming `'static`, because for instance `&'static &'a T` -will fail to typecheck, but the unbound lifetime will perfectly mold into -`&'a &'a T` as needed. However for most intents and purposes, such an unbounded -lifetime can be regarded as `'static`. +Such lifetimes come into the world as *unbounded*. The most common source of +this is taking a reference to a dereferenced raw pointer, which produces a +reference with an unbounded lifetime. Such a lifetime becomes as big as context +demands. This is in fact more powerful than simply becoming `'static`, because +for instance `&'static &'a T` will fail to typecheck, but the unbound lifetime +will perfectly mold into `&'a &'a T` as needed. However for most intents and +purposes, such an unbounded lifetime can be regarded as `'static`. Almost no reference is `'static`, so this is probably wrong. `transmute` and `transmute_copy` are the two other primary offenders. One should endeavor to @@ -17,17 +17,25 @@ boundaries. Given a function, any output lifetimes that don't derive from inputs are unbounded. For instance: - -```rust,ignore -fn get_str<'a>() -> &'a str; + +```rust,no_run +fn get_str<'a>(s: *const String) -> &'a str { + unsafe { &*s } +} + +fn main() { + let soon_dropped = String::from("hello"); + let dangling = get_str(&soon_dropped); + drop(soon_dropped); + println!("Invalid str: {}", dangling); // Invalid str: gӚ_` +} ``` -will produce an `&str` with an unbounded lifetime. The easiest way to avoid -unbounded lifetimes is to use lifetime elision at the function boundary. -If an output lifetime is elided, then it *must* be bounded by an input lifetime. -Of course it might be bounded by the *wrong* lifetime, but this will usually -just cause a compiler error, rather than allow memory safety to be trivially -violated. +The easiest way to avoid unbounded lifetimes is to use lifetime elision at the +function boundary. If an output lifetime is elided, then it *must* be bounded by +an input lifetime. Of course it might be bounded by the *wrong* lifetime, but +this will usually just cause a compiler error, rather than allow memory safety +to be trivially violated. Within a function, bounding lifetimes is more error-prone. The safest and easiest way to bound a lifetime is to return it from a function with a bound lifetime.