What is wrong with `f64::from` in generic context?

If I write such code it compiles just fine:

fn main() {
    let x: u32 = 17;
    let y: f64 = f64::from(x);
}

but the same code inside generic function cause compile time error:

fn f<T>(arg: T)
where
    f64: From<T>,
{
    let x: u32 = 17;
    let y: f64 = f64::from(x);
}
error[E0308]: mismatched types
   --> src/main.rs:15:28
    |
10  | fn f<T>(arg: T)
    |      - this type parameter
...
15  |     let y: f64 = f64::from(x);
    |                  --------- ^ expected type parameter `T`, found `u32`
    |                  |
    |                  arguments to this function are incorrect
    |
    = note: expected type parameter `T`
                         found type `u32`

I can not understand what is going on.
The variable arg of type T is not used, but some how compiler expects to find it?

I've never seen this before, and I'm not sure if the compiler is supposed to do this, but the problem disappears if you comment out the where clause, change it to f64: From<u32>, or if you change the from call to <f64 as From<u32>>::from(x).

My assumption is that when you specify a constraint like that, it "shadows" the other ones. The as syntax there lets you specify exactly which implementation of From you want to use.

2 Likes

Looks like this code has fallen victim to some quirky type inference rules that take trait bounds from the containing function quite strongly into consideration for method resolution.

IIRC, these rules have effects such as making sure that f64::from(x) would work for the f64: From<T> implementation even if the From trait wasn't in scope at all. It's often a good convenience feature. (Edit: I tested it, looks like the call succeeding without the trait in scope may only apply to actual method call syntax.)

Theres nothing really wrong with your code in principle, and your confusion is very reasonable. Type inference in Rust can be tricky - you can always be more explicit to make the error go away. Other changes might change the outcome, too, e. g. if there was a second f64: From<…something else> bound.

For example in this case, the fully explicit <f64 as From<u32>>::from(x) still works fine. In this case, where input and output types are clear from context, even From::from(x) (or x.into()) appear to work fine.

6 Likes

I am obviously need this "where caluse", because of in real code I convert T to f64.
In this example I remove it, because of without conversation T to f64 compilation error still exists.

Sounds like another case of the known issue.

2 Likes

I wonder if the fabled New Trait Solver is going to help with this one.

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.