Description
This came up in #62528, see “Joining strings with char
”.
Reduced test case:
fn takes_str(_x: &str) {}
fn takes_type_parameter<T>(_x: T) where T: SomeTrait {}
trait SomeTrait {}
impl SomeTrait for &'_ str {}
impl SomeTrait for char {}
fn main() {
let string = String::new();
takes_str(&string); // Ok
takes_type_parameter(&string); // Error
}
Output: (Identical in rustc 1.36.0 (a53f9df 2019-07-03) and rustc 1.38.0-nightly (78ca1bd 2019-07-08).)
error[E0277]: the trait bound `&std::string::String: SomeTrait` is not satisfied
--> a.rs:12:5
|
12 | takes_type_parameter(&string); // Error
| ^^^^^^^^^^^^^^^^^^^^ the trait `SomeTrait` is not implemented for `&std::string::String`
|
note: required by `takes_type_parameter`
--> a.rs:3:1
|
3 | fn takes_type_parameter<T>(_: T) where T: SomeTrait {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error
In the takes_str(&string)
call, the type of the _x
parameter is known to be &str
. &String
can be coerced through auto-deref.
In the takes_type_parameter(&string)
call, I imagine that trait resolution finds two solution (the _x
parameter is either &str
or char
) but neither of them match with &string
directly, so trait resolution emits an error. Presumably, coercion are only attempted in a later phase of compilation.
Is there a language design reason it has to be this way, or could we make programs like the above valid? (It then “only” be a matter of compiler implementation.)