Sizedness of Self in traits, blanket implementation and conflicts

I'm trying to better understand the effect of implicit Sized(ness) bounds when it comes to traits and their implementations and I'd like to double check if my reasoning is correct when it comes to the following case

trait Foo {
    fn foo(&self);
}

// No conflicting implementation with this one...
impl Foo for str {
    fn foo(&self) {
        todo!()
    }
}

//... But I get a conflicting implementation error if I uncomment this one
// impl Foo for &str {
//    fn foo(&self) {
//        todo!()
//    }
// }

impl<T> Foo for T
{
    fn foo(&self) {
        todo!()
    }
}

QUESTIONS:

  1. Is it correct to call the impl<T> Foo for T a blanket implementation even if there are no explicit bounds on T? I mean, I thought a blanket implementation was saying: "implement this trait for all types satisfying these bounds", but there are no explicit bounds here... Maybe it's the implicit T: Sized bound that makes it a blanket implementation proper? Or is my definition of blanket implementation wrong?

  2. Let's assume I can call it a blanket implementation: is it correct to think that this code compiles because the implicit T: Sized bound in such blanket implementation prevents it to conflict with the impl Foo for str given that str is !Sized ?

  3. If 1 is correct then the fact that uncommenting the impl Foo for &str causes a compilation error can be explained by saying that &str: Sized and so such implementation is already "covered" by the blanket one?

  4. How to reason about the fact that Self is implicitly ?Sized in the trait definition, but it "becomes" Sized in the blanket implementation? Is it because whenever we are in a generic context, any type placeholder T is always considered to be Sized?

I'm trying to ensure my mental model has no huge flaws here...

Thanks,
Andrea

Yes, a blanket implementation is any impl<T> Foo for T whether or not it has bounds. The part that makes it “blanket” is that it is for T, not for MyType or for Vec<T>.

is it correct to think that this code compiles because the implicit T: Sized bound in such blanket implementation prevents it to conflict with the impl Foo for str given that str is !Sized ?

Yes.

  1. If 1 is correct then the fact that uncommenting the impl Foo for &str causes a compilation error can be explained by saying that &str: Sized and so such implementation is already "covered" by the blanket one?

Yes.

  1. How to reason about the fact that Self is implicitly ?Sized in the trait definition, but it "becomes" Sized in the blanket implementation? Is it because whenever we are in a generic context, any type placeholder T is always considered to be Sized?

I would put it this way: when you declare a type parameter, such as T in impl<T>, it has an implicit Sized bound unless you opt out with ?Sized. That happens only for explicitly declared type parameters; Self is not one of those, so it is not implicitly Sized.

(The language could have been defined so that trait Foo {} meant trait Foo: Sized {} unless you opted out, but it isn't.)

4 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.