在Hyper的示例中,有一些代码可以用成功编译的类型进行注释:
driver.find_element_by_xpath("//td[@class='CreateQuoteBigButtonCell3' and @id='pt1:r1:0:cf16cl']").click()
.map_err(|x: std::io::Error| -> hyper::Error {
::std::convert::From::<std::io::Error>::from(x)
})
的类型定义似乎是From::from()
当我给出的任何泛型和参数都不是fn from(T) -> Self;
类型时,看起来似乎std::io::Error -> Self
似乎返回hyper::Error
值是怎么回事?
即使我明确指定了所有类型,似乎也会发生某种隐式类型转换?
答案 0 :(得分:6)
Rust中的类型信息可以向后流动。
闭包的返回类型指定为hyper::Error
。因此,块的结果必须为hyper::Error
,因此From::from
的结果必须为hyper::Error
。
如果您愿意,可以使用......
<hyper::Error as ::std::convert::From>::<std::io::Error>::from(x)
......这将是更完全合格的版本。但是在那里有闭包返回类型,这是不必要的。
答案 1 :(得分:3)
类型推断有不同程度。
例如,在C ++中,每个文字都是类型化的,并且只能实例化完全形成的类型,因此可以计算任何表达式的类型(并且是)。在C ++ 11之前,这导致编译器给出错误消息:您正在尝试将类型X
的值分配给类型为Y
的变量。在C ++ 11中,引入了auto
,让编译器根据分配给它的值来计算变量的类型。
在Java中,这种方式略有不同:变量的类型必须完全拼写出来,但在构造类型时,可以省略通用位,因为它们是从赋值的变量中推导出来的。
这两个例子很有意思,因为类型信息在两者中都不会以相同的方式流动,这暗示了流程没有理由以这种或那种方式流动;但是有很多技术限制。
相反,Rust使用了Hindley Milner类型统一算法的变体。我个人认为Hindley Milner是一个等式系统:
例如,想象一下:
fn print_slice(s: &[u32]) {
println!("{:?}", s);
}
fn main() {
let mut v = Vec::new();
v.push(1);
print_slice(&v);
}
从main
开始:
v => A
,1 => B
,A = Vec<C>
(来自v = Vec::new()
),C = B
(来自v.push(1)
),A = &[u32]
或<A as Deref>::Output = &[u32]
或{{ 1}}(来自...
,print_slice(&v)
,A = Vec<B>
,&[B] = &[u32]
,B = u32
。由于子类型(原来的HM没有),混合编织有一些困难,但它基本上就是这样。
在这个过程中,没有考虑后退或转发,它只是解决方程式。
此过程称为类型统一,如果失败,则会出现希望有用的编译器错误。