Deref coercion is based around a known target type, and doesn't do trait solving. If it did, many more use cases would become ambiguous. Probably approximately all use cases, if forward compatibility were to be considered.
Hmmm. Is it technically impossible for all circumstances? Probably not, no.
My take is that the documentation in question is more of a "btw this situation doesn't work" than some formal justification on why it couldn't work, or shouldn't work.
That said, I don't actually know exactly what the documentation author meant with their phrasing.
My vibe is more along the lines of "some functionality is hindered by generics" (this example, implicit reborrows...), but I'm afraid I don't have a citation or fleshed-out mental model to offer in this case.
Rust only performs deref coercions if the target type is known unambiguously. That isn't the case when it's a generic, so no coercions happen at all in that scenario.
Do you mean the information that only &str implements the trait? No because rustc tries to be robust in case you add more implementations in the future.
Tomorrow you might add
impl<'a> TraitExample for &'a String {}
and that should not silently change the behavior of example_func(&example_string).
I know that there are some exceptions to this principle, but it does apply most of the time.
...and then you add the implementation described by Alice, and this call silently changes its behavior. Which, in general, is treated as worse than even explicit breakage.
No, it's not the same. Think about a multi-crate scenario and who breaks who.
// Crate A.
trait MyTrait {
fn foo(&self);
}
/// Crate B.
struct MyStruct;
impl MyTrait for MyStruct {
fn foo(&self);
}
If we add an inherent foo method to MyStruct, then that change happened in crate B, so crate B broke itself. Not great, but acceptable.
On the other hand, consider our original scenario:
// Crate A.
trait TraitExample {}
impl<'a> TraitExample for &'a str {}
fn example_func<A: TraitExample>(example_arg: A) {}
// Crate B.
let example_string = String::from("example_string");
example_func(&example_string);
If someone adds an impl TraitExample for &String, then that happens in crate A due to the orphan rule. So it would be crate A breaking a downstream crate B. We want to design Rust such that adding a new implementation of a trait is not a breaking change, but if crate B used deref coercion when calling example_func, then it would be a breaking change. Therefore, we don't use deref coercion.