Lifetime elision

Viewed 44

I have struct

struct Cursor<'a> {
    chars: Chars<'a>,
}

what's the difference between

impl Cursor<'_> {
    fn new(input: &str) -> Cursor { ... }
}

and

impl<'a> Cursor<'a> {
    fn new(input: &'a str) -> Cursor<'a> { ... }
}

?

2 Answers

The difference is that the first version is desugared as:

impl<'a> Cursor<'a> {
    fn new<'b>(input: &'b str) -> Cursor<'b> { ... }
}

That is, there are two distinct lifetimes.

However, I'm having hard time imagining any actual difference. The compiler will just infer to 'a to be some lifetime, and you'll work only with 'b, just like you work with 'a in the second version.

It's to do with lifetime elision. To quote The Edition Guide:

Rust 2018 allows you to explicitly mark where a lifetime is elided, for types where this elision might otherwise be unclear. To do this, you can use the special lifetime '_ much like you can explicitly mark that a type is inferred with the syntax let x: _ = ..;.

Let's say, for whatever reason, that we have a simple wrapper around &'a str:

struct StrWrap<'a>(&'a str);

In Rust 2015, you might have written:

use std::fmt;

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:

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)
    }
}
Related