Why does `PathBuf` conflict with this generic impl but not `Path`

Could someone explain why the Path implementation works, but not the PathBuf one?

trait Something {}
impl<T> Something for T where T: Display {}

impl Something for Path {} // <- does not conflict with above generic impl?
impl Something for PathBuf {} // <- conflicts with above generic impl?

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=095f9cbfc1c0b9ed18394882b2d64ed8

I didn't think either of those implemented Display.

Because Path is not sized, so the blanket impl does not apply. If you say that T is allowed to not be Sized, then they will conflict.

impl<T: ?Sized> Something for T where T: Display {}
impl Something for Path {}

playground

It doesn't matter if Path implements Display or not — it is not a breaking change for std to add it later, so orphan rules would prevent you even if it doesn't impl Display.

4 Likes

aah there is some subtlety here I don't fully understand.

So as I understand it: if you don't control a type, you are not allowed to assume it does not implement a trait, so that the owner can implement it later?

See that's just straight-up confusing rather than clarifying... well, anything at all.

It should matter whether Path and PathBuf implement Display or not, given the trait bound on the blanket impl.

If what you say is true, then a programmer can't derive any information at all about why a certain impl conflicts with another.
For example, rustc didn't even mention Sizedness, so at the very least it's a failure of rustc to properly report the error.

If it mattered whether Path implemented the trait, then it would be a breaking change to add any impl to any type in the standard library, and this is not desirable. If this was regarding a type defined in the same crate, then it does indeed matter, because then the crate defining MyType isn't just going to randomly add the impl.

struct MyType;

trait Something {}
impl<T: ?Sized> Something for T where T: Display {}

// this is ok, MyType is from this crate
impl Something for MyType {}

playground

Similarly if Display was replaced with a non-foreign trait, then it would also be ok:

trait MyDisplay {}

trait Something {}
impl<T: ?Sized> Something for T where T: MyDisplay {}

// this is ok, Path won't add an impl MyDisplay
impl Something for Path {}

playground

I mean, in this case the impl with Path didn't produce an error in the first place, and I'm not sure why the other error warrants a mention of Sized.

1 Like