'_, the anonymous lifetime
Rust 2018 allows you to explicitly mark where a lifetime is elided, for typeswhere this elision might otherwise be unclear. To do this, you can use thespecial lifetime '
much like you can explicitly mark that a type is inferredwith the syntax let x:
= ..;
.
Let's say, for whatever reason, that we have a simple wrapper around &'a str
:
#![allow(unused_variables)]
fn main() {
struct StrWrap<'a>(&'a str);
}
In Rust 2015, you might have written:
#![allow(unused_variables)]
fn main() {
use std::fmt;
struct StrWrap<'a>(&'a str);
// Rust 2015
fn make_wrapper(string: &str) -> StrWrap {
StrWrap(string)
}
impl<'a> fmt::Debug for StrWrap<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.write_str(self.0)
}
}
}
In Rust 2018, you can instead write:
#![allow(unused_variables)]
fn main() {
use std::fmt;
struct StrWrap<'a>(&'a str);
// Rust 2018
fn make_wrapper(string: &str) -> StrWrap<'_> {
StrWrap(string)
}
impl fmt::Debug for StrWrap<'_> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.write_str(self.0)
}
}
}
More details
In the Rust 2015 snippet above, we've used -> StrWrap
. However, unless you takea look at the definition of StrWrap
, it is not clear that the returned valueis actually borrowing something. Therefore, starting with Rust 2018, it isdeprecated to leave off the lifetime parameters for non-reference-types (typesother than &
and &mut
). Instead, where you previously wrote -> StrWrap
,you should now write -> StrWrap<'_>
, making clear that borrowing is occurring.
What exactly does '_
mean? It depends on the context!In output contexts, as in the return type of make_wrapper
,it refers to a single lifetime for all "output" locations.In input contexts, a fresh lifetime is generated for each "input location".More concretely, to understand input contexts, consider the following example:
#![allow(unused_variables)]
fn main() {
// Rust 2015
struct Foo<'a, 'b: 'a> {
field: &'a &'b str,
}
impl<'a, 'b: 'a> Foo<'a, 'b> {
// some methods...
}
}
We can rewrite this as:
#![allow(unused_variables)]
fn main() {
struct Foo<'a, 'b: 'a> {
field: &'a &'b str,
}
// Rust 2018
impl Foo<'_, '_> {
// some methods...
}
}
This is the same, because for each '_
, a fresh lifetime is generated.Finally, the relationship 'a: 'b
which the struct requires must be upheld.
For more details, see the tracking issue on In-band lifetime bindings.