简体   繁体   中英

Rust error: unable to infer enough type information to locate the impl of the trait

I don't quite understand why this is ok

use std::num;

fn main() {
    let mut p_: int = num::pow(16, 2);
    let mut p: f64 = p_ as f64;
}

but this is failing

use std::num;

fn main() {
    let mut p: f64 = num::pow(16, 2) as f64;
}

with error

error: unable to infer enough type information to locate the impl of the trait `core::num::Int` for the type `_`; type annotations required
let mut p: f64 = num::pow(16, 2) as f64;
                 ^~~~~~~~

For the sake of context, this is what I'm trying to do (pi approximation):

fn main() {
    let mut pi: f64 = 0.0;
    let precision: int = 10;

    for i in range(0, precision) {
        let k = i as f64;
        let mut pi16: int = num::pow(16, i as uint);
        let mut p16: f64 = pi16 as f64;
        pi += 1.0/p16 * (4.0/(8.0 * k + 1.0) - 2.0/(8.0 * k + 4.0) - 1.0/(8.0 * k + 5.0) - 1.0/(8.0 * k + 6.0));
    }
}

I'm using rustc 0.13.0-nightly (f09279395 2014-11-17 17:22:06 +0000)

Update: as of Rust 1.0.0-alpha, pow() function is now a method on Int trait, and Float trait also provides powf() and powi() methods for raising into floating point and integer powers, respectively. Also uint has been changed to usize . Consequently, here's how power operation looks now:

let mut p: f64 = 16us.pow(2) as f64;

It is also possible to use floating point operations to operate directly on f64 :

let mut p: f64 = 16.0.powi(2);

The basic idea of the answer, however, still holds: you still need to specify exact types on literals in order for the method to be found at all.


Let's look on num::pow() signature:

pub fn pow<T: Int>(base: T, exp: uint) -> T

It requires that the first argument implements Int trait, and it will return the value of the same type as this first argument. Int implementors are listed here :

impl Int for u8
impl Int for u16
impl Int for u32
impl Int for u64
impl Int for uint
impl Int for i8
impl Int for i16
impl Int for i32
impl Int for i64
impl Int for int

It means that this function will work for any of these types. Now, let's look to your examples.

This works:

use std::num;

fn main() {
    let mut p_: int = num::pow(16, 2);
    let mut p: f64 = p_ as f64;
}

because there are no ambiguities. Rust compiler correctly infers that num::pow() is invoked as num::pow::<int>() because you explicitly stated that p_ is int , which constrains num::pow() return type to int and hence its parameter should also be int .

In this case, however, there is an ambiguity:

use std::num;

fn main() {
    let mut p: f64 = num::pow(16, 2) as f64;
}

Integer literals are untyped, so the compiler does not know exact type of 16 . This is the error you see: it can't decide which implementation to use because there are several appropriate types. You need to put the literal type explicitly:

use std::num;

fn main() {
    let mut p: f64 = num::pow(16u, 2) as f64;
}

or

use std::num;

fn main() {
    let mut p: f64 = num::pow::<uint>(16, 2) as f64;
}

and it will work.

This is type inference at play.

Here is the signature of pow :

pub fn pow<T: Int>(base: T, exp: uint) -> T

In

let p: int = num::pow(16, 2);

The result is int , so therefore 16 is also int since they have the same type (according to the signature).

In

num::pow(16, 2) as f64

however, you ask the result to be converted to f64 , but never precise from what it should convert.

If precising the type of the result is a problem (requiring additional variables), then you can either:

// precise the type at call site
num::pow::<i64>(16, 2)

// precise the type of the argument
num::pow(16i64, 2)

I would obviously note that precising the type of the argument is easier.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM