rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
98.56k stars 12.74k forks source link

Bad "Ambiguous Numeric Type" Recommendation from Compiler in for loop #53572

Open spwitt opened 6 years ago

spwitt commented 6 years ago

The pow function cannot be called on an ambiguous numeric type. If pow is called on the variable of a for loop, the compiler's recommended solution to add a concrete numeric type does not compile.

Example code with ambiguous numeric type:

pub fn check() {
    for i in 0..1000 {
        println!("{}", i.pow(2));
    }
}

Gives the error:

error[E0689]: can't call method `pow` on ambiguous numeric type `{integer}`
  --> src/lib.rs:22:26
   |
22 |         println!("{}", i.pow(2));
   |                          ^^^
help: you must specify a type for this binding, like `i32`
   |
21 |     for i: i32 in 0..1000 {
   |         ^^^^^^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0689`

Implementing this recommendation by adding type to variable i as shown in the compiler recommendation and trying to compile again gives the error:

error: missing `in` in `for` loop
  --> src/lib.rs:21:10
   |
21 |     for i: i32 in 0..1000 {
   |          ^ help: try adding `in` here

error: expected expression, found `:`
  --> src/lib.rs:21:10
   |
21 |     for i: i32 in 0..1000 {
   |          ^ expected expression

error: aborting due to 2 previous errors

Not sure if there is a better solution, but adding a cast to the range rather than specifying the type of the variable worked for me:

pub fn check() {
    for i in 0..1000 as i32 {
        println!("{}", i.pow(2));
    }
}
varkor commented 6 years ago

If https://github.com/rust-lang/rfcs/pull/2522 is accepted, the suggestion should work as is.

leonardo-m commented 6 years ago

Beside rfc2522, it's better to minimize the usages of as casts, so this is better:

for i in 0i32 .. 1000 {

spwitt commented 6 years ago

Is there a reason to avoid as other than 0i32 being cleaner and more idiomatic? Those are good enough reasons. Just wondering if there are performance implications using as or other reasons to avoid it.

Given there is a good workaround and a forthcoming change, should this issue stay open or be closed? This is my first github issue and second week using Rust.

varkor commented 6 years ago

@spwitt: this issue can stay open! This is currently a valid issue with the diagnostics: it might just be worth waiting for the RFC (or making a temporary fix now).

Is there a reason to avoid as other than 0i32 being cleaner and more idiomatic?

In this example, no: all as i32 is doing is directing the type inference. 0i32 just looks slightly cleaner.

estebank commented 6 years ago

The incorrect suggestion is "fixed" in nightly since #51670:

error[E0689]: can't call method `pow` on ambiguous numeric type `{integer}`
 --> src/lib.rs:3:26
  |
2 |     for i in 0..1000 {
  |         - you must specify a type for this binding, like `i32`
3 |         println!("{}", i.pow(2));
  |                          ^^^

Just wondering if there are performance implications using as or other reasons to avoid it.

Casting (as) has some tricky semantics. I particularly prefer using .into() when available (and that conversion is available only when as would always work as expected). When using as on literals it is fine for the most part, but then you already have the syntax proposed by @vankor which ends up looking cleaner. The only case where you'd need as (until TryFrom is available in stable) is when trying to downcast or cast to and from isize/usize.

estebank commented 6 years ago

@varkor I'm inclined to close this as duplicate of #51972 and #51634, but we probably should still improve the output to be

error[E0689]: can't call method `pow` on ambiguous numeric type `{integer}`
 --> src/lib.rs:3:26
  |
2 |     for i in 0..1000 {
  |         -    - help: give this literal a type: `0i32`
  |         |
  |         you must specify a type for this binding, like `i32`
3 |         println!("{}", i.pow(2));
  |                          ^^^ can't call this method on ambiguous numeric type

cc @zackmdavis who proposed that in one of the linked tickets.

leonardo-m commented 6 years ago

Is there a reason to avoid as other than 0i32 being cleaner and more idiomatic

The use of "as" should be minimized in Rust code because Rust doesn't perform overflow tests for "as" casts even in debug builds. It's a significant Rust design mistake.

DoumanAsh commented 3 years ago

It is a bit off topic, but is it intended that compiler is unable to infer integer type from method usage? For example 0.wrapping_shl(1u32);

Z2Up1UwcaYOyZq commented 1 year ago

Issue is still valid

estebank commented 12 months ago

Current output:

error[E0689]: can't call method `pow` on ambiguous numeric type `{integer}`
 --> src/lib.rs:3:26
  |
2 |     for i in 0..1000 {
  |         - you must specify a type for this binding, like `i32`
3 |         println!("{}", i.pow(2));
  |                          ^^^