此代码有效:
fn main() {
let a: i32 = (1i32..10).sum();
let b = a.pow(2);
}
如果我从i32
删除a
类型,则会收到此错误:
rustc 1.13.0 (2c6933acc 2016-11-07)
error: the type of this value must be known in this context
--> <anon>:3:13
|
5 | let b = a.pow(2);
| ^^^^^^^^
我原以为Rust会将(1i32..10)
转换为i32
迭代器,然后sum()
知道返回i32
。我错过了什么?
答案 0 :(得分:11)
定义sum
的方式,返回值是开放式的;多个类型可以实现特征Sum<i32>
。这是一个使用a
的不同类型的示例,两者都编译:
#[derive(Clone, Copy)]
struct Summer {
s: isize,
}
impl Summer {
fn pow(&self, p: isize) {
println!("pow({})", p);
}
}
impl std::iter::Sum<i32> for Summer {
fn sum<I>(iter: I) -> Self
where
I: Iterator<Item = i32>,
{
let mut result = 0isize;
for v in iter {
result += v as isize;
}
Summer { s: result }
}
}
fn main() {
let a1: i32 = (1i32..10).sum();
let a2: Summer = (1i32..10).sum();
let b1 = a1.pow(2);
let b2 = a2.pow(2);
}
由于两种结果类型都是可能的,因此无法推断出类型,必须通过turbofish(sum::<X>()
)或表达式(let x: X = ...sum();
)的结果明确指定类型。
答案 1 :(得分:7)
然后
sum()
知道返回i32
这是关键缺失点。虽然&#34;输入&#34;类型已经知道(它必须是实现Iterator
才能使sum
可用),&#34;输出&#34;类型非常灵活。
fn sum<S>(self) -> S
where
S: Sum<Self::Item>,
它返回一个必须实现Sum
的泛型S
。 S
不必须与Self::Item
匹配。因此,编译器要求您指定什么
输入的类型。
为什么这有用?查看标准库中的这两个示例实现:
impl Sum<i8> for i8
impl<'a> Sum<&'a i8> for i8
那是对的!您可以总结一个u8
或迭代器&u8
的迭代器!如果我们没有这个,那么这段代码将不起作用:
fn main() {
let a: i32 = (0..5).sum();
let b: i32 = [0, 1, 2, 3, 4].iter().sum();
assert_eq!(a, b);
}
As bluss points out,我们可以通过使用关联类型来实现此目标,该类型会绑定u8 -> u8
和&'a u8 -> u8
。
如果我们只有一个关联的类型,那么目标总和类型将始终是固定的,我们将失去灵活性。有关详细信息,请参阅When is it appropriate to use an associated type versus a generic type?。
作为示例,我们还可以为我们自己的类型实现Sum<u8>
。在这里,我们总结了u8
s,但增加了我们求和的类型的大小,因为它的总和可能会超过u8
。此实现是添加到标准库中的现有实现:
#[derive(Debug, Copy, Clone)]
struct Points(i32);
impl std::iter::Sum<u8> for Points {
fn sum<I>(iter: I) -> Points
where
I: Iterator<Item = u8>,
{
let mut pts = Points(0);
for v in iter {
pts.0 += v as i32;
}
pts
}
}
fn main() {
let total: Points = (0u8..42u8).sum();
println!("{:?}", total);
}