Search code examples
syntaxrusttraitstrait-objects

What does `impl TraitX for TraitY` mean in Rust?


For example:

trait TraitX { }
trait TraitY { }
impl TraitX for TraitY { }

I figured it would mean the same as

impl<A: TraitY> TraitX for A { }

but the error message suggests otherwise:

$ rustc --version
rustc 0.12.0-nightly (a70a0374e 2014-10-01 21:27:19 +0000)
$ rustc test.rs
test.rs:3:17: 3:23 error: explicit lifetime bound required
test.rs:3 impl TraitX for TraitY { }
                          ^~~~~~

Does impl TraitX for TraitY (or some variant of it with an explicit lifetime) mean anything in Rust? If so, what is an example of its use?


Solution

  • Rust has changed a lot since this question was asked. While that syntax is currently* still supported, trait objects should now be designated with the keyword dyn:

    trait TraitX { }
    trait TraitY { }
    impl TraitX for dyn TraitY { }
    

    This is entirely equivalent to the code in the question, but a bit more obvious about what it means: implement TraitX for the trait object dyn TraitY.

    For example:

    struct Thing;
    impl TraitY for Thing {}
    
    fn main() {
        // Treat the &Thing as a dynamic object
        let object: &dyn TraitY = &Thing;
    
        // The TraitY object can still be used where a TraitX is expected
        do_something(object);
    }
    
    fn do_something<T: TraitX + ?Sized>(t: &T) {
    }
    

    On the surface, as you mention, it seems similar to:

    impl<A: TraitY> TraitX for A { }
    

    This implements TraitX for any concrete type that implements TraitY, but does not include trait objects because Sized is always an implicit bound on type parameters. But we can remove that limitation by explicitly opting out of the Sized bound:

    impl<A: TraitY + ?Sized> TraitX for A { }
    

    This includes all concrete types that implement TraitY, but now also includes dynamic TraitY objects. For maximum flexibility, you should use this form instead of either of the alternatives above.


    *"Currently" because future editions of Rust may require the keyword in these situations. At least, the default linter will disallow omitting it.