2014-10-10 94 views
2

我试图跟随guide,而且我编译如下代码:为什么random()结果不具有推断的类型?

use std::rand; 

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

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

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

不过,我得到以下错误:

➜ 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. 

我看着%定义,原来它使用Rem trait。特质仅在相同类型的操作数上定义操作符。

现在,我使用100i作为第二操作数,根据reference应该是int

那么,为什么编译器无法根据random()推断出正确的类型,正如手册所示? (和我的Haskell经验暗示它应该)。

不完全确定这是一个错误,因此是一个问题。

一些额外的信息:在Ubuntu 14.04 64

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

上运行。

更新:我注意到这个错误是打算发生的(该指南告诉稍后修复它的方法)。然而,为什么编译器不能推断该类型的原始问题仍然适用。

+4

这非常有趣...请注意,如果您交换参数的顺序(即'100i%rand :: random()'),它会正确推断类型。我对这种行为也很好奇 – aochagavia 2014-10-10 13:55:51

回答

2

那么,为什么编译器无法推断出类型的答案很简单。这是Rem定义:

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

注意,它有两个类型参数,RHSResult。每个性状还具有暗示的类型参数Self,它指定了实现特征的类型。这是Rem实施int的样子:

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

所以在这里Self = intRHS = intResult = int。但是特质是开放的,也就是说,你可以实现任何类型的外国特质,你可以为任何外国类型实现你自己的特质。没有人能阻止你添加一个实现这样一个(Self = XRHS = intResult = int):

struct X; 

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

现在rand::random()调用不明确:应类型检查选择rand::random::<X>()rand::random::<int>()

请注意,理论上类型检查器可以决定使用唯一适用于这种情况的类型。但是,这会导致非常脆弱的计划。假设情况是这样,并且原始程序正常编译。在相同的模块中,但在不相关的部分中,您使用其他类型,例如X,这是从另一个库导入的。然后这个图书馆的作者突然决定,如果X实施Rem<int, int>会很好。因为导入一个类型也会导入该类型的所有trait实现,然后BAM,您的程序突然停止编译。

这可能是好的,如果它是你的程序。毕竟,你总是可以注意到这样的编译错误并相应地纠正它。但是,假设这不是发生在您的程序中,而是发生在您依赖的库中。例如,如果使用libyXlibx,然后libx笔者决定将违规的特质实施X,然后liby突然停止编译并没有什么可以做。这意味着,例如,您将无法轻松修改库版本。

+0

但是,为什么不能选择'int'专业化,当没有外部特征实现?所有它必须考虑的是内置的特质实现,其中只有一个匹配。 – 2014-10-10 18:23:46

+0

因为这会让你的程序非常脆弱。想象一下,你从另一个库中导入某种类型。一切都很好,你可以在模块的完全不相关的部分使用它。然后上游作者决定为这种类型实施'Rem '是个好主意。因为导入类型也意味着导入该类型的所有特征实现,所以程序会突然停止编译。如果它是你的程序也许可以,但如果它是另一个你不能控制但又依赖的库?仅仅禁止这种推断是安全得多的。 – 2014-10-10 18:29:00

+0

我已经更新了答案。 – 2014-10-10 18:38:13