4
votes

I have this question long ago since from day 1 I started to learn rust. I learned that the implementation for std::fmt::Debug has a function signature

fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result.

At first I just copy this signature and treat it as a standard boilerplate code. However, as I learned more I realized that <'_> means lifetime elision. I did some research and according to issue #49469 <'_> can let the return value infer its lifetime according to parameters (which is really cool). But I also see people use <'_> extensively with fmt::Formatter, such as the standard library document and mio, which in these cases <'_> should not change the default lifetime inference behavior. In addition, I did a quick test with the following code

use std::fmt;

struct Test();

impl fmt::Debug for Test {
    fn fmt(&self,fmt:&mut fmt::Formatter) -> fmt::Result {
        write!(fmt,"test")?;
        Ok(())
    }
}

fn main() {
    let t = Test();
    println!("{:?}",t);
}

and it compiles and runs. So does <'_> here have some special usages with some edge cases I don't know?

Thanks in advance.

1

1 Answers

5
votes

From Rust RFC 2115: Argument Lifetimes:

You can write '_ to explicitly elide a lifetime, and it is deprecated to entirely leave off lifetime arguments for non-& types.

And from the included motivation:

[A] point of confusion for newcomers and old hands alike is the fact that you can leave off lifetime parameters for types:

struct Iter<'a> { ... }

impl SomeType {
    // Iter here implicitly takes the lifetime from &self
    fn iter(&self) -> Iter { ... }

As detailed in the ergonomics initiative blog post, this bit of lifetime elision is considered a mistake: it makes it difficult to see at a glance that borrowing is occurring, especially if you're unfamiliar with the types involved. (The & types, by contrast, are universally known to involve borrowing.)

In summary, you should use fmt::Formatter<'_>.