'_, the anonymous lifetime

Minimum Rust version: 1.31

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:

  1. #![allow(unused_variables)]
  2. fn main() {
  3. struct StrWrap<'a>(&'a str);
  4. }

In Rust 2015, you might have written:

  1. #![allow(unused_variables)]
  2. fn main() {
  3. use std::fmt;
  4. struct StrWrap<'a>(&'a str);
  5. // Rust 2015
  6. fn make_wrapper(string: &str) -> StrWrap {
  7. StrWrap(string)
  8. }
  9. impl<'a> fmt::Debug for StrWrap<'a> {
  10. fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
  11. fmt.write_str(self.0)
  12. }
  13. }
  14. }

In Rust 2018, you can instead write:

  1. #![allow(unused_variables)]
  2. fn main() {
  3. use std::fmt;
  4. struct StrWrap<'a>(&'a str);
  5. // Rust 2018
  6. fn make_wrapper(string: &str) -> StrWrap<'_> {
  7. StrWrap(string)
  8. }
  9. impl fmt::Debug for StrWrap<'_> {
  10. fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
  11. fmt.write_str(self.0)
  12. }
  13. }
  14. }

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:

  1. #![allow(unused_variables)]
  2. fn main() {
  3. // Rust 2015
  4. struct Foo<'a, 'b: 'a> {
  5. field: &'a &'b str,
  6. }
  7. impl<'a, 'b: 'a> Foo<'a, 'b> {
  8. // some methods...
  9. }
  10. }

We can rewrite this as:

  1. #![allow(unused_variables)]
  2. fn main() {
  3. struct Foo<'a, 'b: 'a> {
  4. field: &'a &'b str,
  5. }
  6. // Rust 2018
  7. impl Foo<'_, '_> {
  8. // some methods...
  9. }
  10. }

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.