Why type need to be specified manually in this code, in rust?

74 views Asked by At

Rust Playground

trait B<T> {}
impl<T> B<T> for T {}

fn f<K>()
where
    // u32: B<u32>, // 4
    u32: B<K>, // 3
{
    // get::<u32>(&1u32); // 2
    get(&1u32); // 1 error
}

fn get<Q>(k: &Q)
where
    u32: B<Q>,
{
}

fn main() {}

error at 1:

error[E0308]: mismatched types
  --> src/main.rs:10:9
   |
4  | fn f<K>()
   |      - expected this type parameter
...
10 |     get(&1u32); // 1 error
   |     --- ^^^^^ expected `&K`, found `&u32`
   |     |
   |     arguments to this function are incorrect
   |
   = note: expected reference `&K`
              found reference `&u32`

However, &u32 also satisfy trait bound of get.

if I change 1 to 2, it is OK.

if I remove 3, it is OK.

if I add 4, it is OK.

Why?

1

There are 1 answers

0
Chayim Friedman On BEST ANSWER

This is a case of a somewhat less-known detail of Rust's inference: if there are two possible method candidates, and one of them comes from a trait bound of the function (and the other does not), Rust prefers the trait bound's method, unless explicitly told otherwise.

In your case, there are two possible candidates for get::<_>(&u32) (written as get(&1u32)) in the generic parameter Q. The first is to choose Q = u32, the second being Q = K. Since the method requires u32: B<Q>, even though both satisfy this bound, Q = K is selected, because it comes from a trait bound in the caller method, while Q = u32 comes from the environment (the impl impl<T> B<T> for T).

This special case is done to aid inference in cases like the following:

fn foo<T: Into<String>>(v: T) {
    let s = v.into();
}

Here, the programmer obviously wants s to have type String. However, there are actually two candidates here: one being <T as Into<String>>::into(), but the other is the reflexive impl <T as Into<T>>::into(), provided by std. In order for this to not be ambiguous, Rust prefers the trait bound unless explicitly told otherwise.