What does impl trait mean?


#1

Maybe I forgot something. I have been “away from Rust” for a few weeks.
I browsed the source code of the Error trait and saw this:

    impl Error + 'static {
        /// Returns true if the boxed type is the same as `T`
        #[stable(feature = "error_downcast", since = "1.3.0")]
        #[inline]
        pub fn is<T: Error + 'static>(&self) -> bool {
            // Get TypeId of the type this function is instantiated with
            let t = TypeId::of::<T>();

            // Get TypeId of the type in the trait object
            let boxed = self.type_id();

            // Compare both TypeIds on equality
            t == boxed
        }
    ...

I am particularly interested in the impl Error + 'static here. Error is trait. How can I impl a trait?


#2

I was confused over it as well :slight_smile: Confusion about impls without for

In Rust, pure T denotes a trait object type.


#3

Thank you.:slightly_smiling_face:

So that implements methods for the trait object. In the case Error + 'static that means methods on Box<Error>. Am I right?

Also, isn’t the 'static bound inferred if I don’t specify it for a trait object? So impl Error + 'static is actually the same as impl Error?


#4

Yep. Or on &Error.

Also, isn’t the 'static bound inferred if I don’t specify it for a trait object? So impl Error + 'static is actually the same as impl Error?

Not sure about this one. I think Box<Error> is a special case of lifetime elision.


#5

I think Box<Trait> is implicitly Box<Trait + 'static>. I’d imagine Box<Error> is no different.

My guess about that is that impl Error + 'static is a leftover from before default trait bounds were defined, but I’m not certain either.


#6

I don’t want to transform this into a grokking-lifetime-issue but I created an example and I don’t really understand the result:

    trait Foo {
    }

    impl Foo {
        fn lol(&self) { println!("Hello, world"); }
    }

    impl Foo for Bar {}

    #[derive(Debug)]
    struct Bar {
        v: i32,
    }

    fn main() {
        let b = Bar { v: 100 };
        let o: &Foo = &b;
        o.lol();
    }

    fn does_not_work(o: &Foo) {
        o.lol();
    }

With the function does_not_work(o: &Foo) I get my expected result. The method lol() requires &'static Foo. It does not work for an arbitrary lifetime, because the impl Foo mandates 'static. But why does the &Foo in the main function work? Is o an &'static Foo there? Is this some kind of static promotion behind the scenes?


#7

You absolutely should - you and others can benefit.

The reason is because Trait + 'static means the type implementing the trait must have no references or only 'static references - it doesn’t mean the lifetime of a reference to the Trait object itself has to be 'static.

Your Bar struct has no references, so it satisfies Trait + 'static when turned into a Foo trait object. That’s why main works.

Your does_not_work function says it takes &Foo, which is essentially &'elided_lifetime(Foo + 'elided_lifetime), but it needs 'static to call lol. Two ways around this:

  1. Use impl<'a> Foo + 'a {...} to allow lol to be called for a trait object with a generic lifetime.
  2. Change does_not_work param to be &(Foo + 'static)

#8

Ah, yes. Trait + 'a concerns the actual erased type of the trait object. Damn it. I actually knew that! Thanks for the great explanation though. :slightly_smiling_face: