Michael Pankov
Michael Pankov

Reputation: 3701

Why random() result doesn't have the type inferred?

I'm trying to follow the guide, and am compiling the following code:

use std::rand;

fn main() {
    println!("Guess the number!");

    let secret_number = (rand::random() % 100i) + 1i;

    println!("The secret number is: {}", secret_number);
}

However, I get the following error:

➜  guess git:(master) ✗ cargo run 
   Compiling guess v0.0.1 (file:///home/mkpankov/rust/guide/guess)
/home/mkpankov/rust/guide/guess/src/main.rs:6:26: 6:40 error: the type of this value must be known in this context
/home/mkpankov/rust/guide/guess/src/main.rs:6     let secret_number = (rand::random() % 100i) + 1i;
                                                                       ^~~~~~~~~~~~~~
error: aborting due to previous error
Could not compile `guess`.

To learn more, run the command again with --verbose.

I looked at definition of %, turns out it uses Rem trait. And the trait defines the operator only on same-typed operands.

Now, I use 100i as second operand, and that should be int, according to reference.

So, why can't compiler infer the proper type requested from random(), as the manual suggests? (and my Haskell experience hints it should).

Not fully sure it's a bug, hence the question.

Some additional info:

➜  guess git:(master) ✗ rustc --version
rustc 0.12.0-nightly (63fe80e1f 2014-10-08 23:42:39 +0000)

Runs on Ubuntu 14.04 x64.

Update: I noticed this error is intended to happen (the guide tells the way to fix it later). However, the original question of why can't the compiler infer the type still applies.

Upvotes: 2

Views: 202

Answers (1)

Vladimir Matveev
Vladimir Matveev

Reputation: 128111

Well, the answer why the compiler can't infer the type is simple. This is Rem definition:

pub trait Rem<RHS, Result> {
    fn rem(&self, rhs: &RHS) -> Result;
}

Note that it takes two type parameter, RHS and Result. Each trait also has implicit type parameter called Self, which designates the type for which the trait is implemented. This is how Rem implementation for int looks like:

impl Rem<int, int> for int { ... }

So here Self = int, RHS = int and Result = int. But traits are open, that is, you can implement foreign trait for any type you own and you can implement your own trait for any foreign type. No one can prevent you for adding an implementation like this one (Self = X, RHS = int, Result = int):

struct X;

impl Rem<int, int> for X {
    fn rem(&self, arg: &int) -> int { *arg }
}

And now rand::random() invocation is ambiguous: should the type checker select rand::random::<X>() or rand::random::<int>()?

Note that in theory the type checker could decide to use the only type which is applicable in this case. However, this would lead to very fragile programs. Suppose that this is the case and the original program compiles normally. In the same module, but in an unrelated part, you use some other type, say, X, which is imported from another library. And then the author of this library suddenly decides that it would be nice if X implemented Rem<int, int>. Because importing a type also imports all trait implementations for that type, then BAM, you program suddenly stops compiling.

It may be fine if it is your program. After all, you can always notice such compilation error and correct it accordingly. However, suppose that this happens not in your program but in a library you depend on. For example, if liby used X from libx, and then libx author decided to add an offending trait implementation for X, then liby suddenly stops compiling and there is nothing you can do. This would mean, for example, that you won't be able to modify library versions easily.

Upvotes: 2

Related Questions