这个使用未装箱的闭包的小型FizzBuzz程序给出了一个相当神秘的错误信息。
fn fizzbuzz<F: Fn(i64) -> bool>(n: i64, f: F, fs: &str, b: F, bs: &str) {
for i in range(1i64, n+1) {
match (f(i), b(i)) {
(true, true) => println!("{:3}: {}{}", i, fs, bs),
(true, _) => println!("{:3}: {}", i, fs),
(_, true) => println!("{:3}: {}", i, bs),
_ => (),
}
}
}
fn main() {
fizzbuzz(30,
|&: i: i64| { i % 3 == 0 }, "fizz",
|&: j: i64| { j % 5 == 0 }, "buzz");
}
错误讯息:
<anon>:15:14: 15:40 error: mismatched types: expected `closure[<anon>:14:14: 14:40]`, found `closure[<anon>:15:14: 15:40]` (expected closure, found a different closure)
<anon>:15 |&: j: i64| { j % 5 == 0 }, "buzz");
^~~~~~~~~~~~~~~~~~~~~~~~~~
error: aborting due to previous error
有人可以描述错误吗?感谢。
答案 0 :(得分:4)
每个未装箱的闭包定义都会创建一个完全不同的类型。这使得main
中定义的每个闭包都是不同的类型。另一方面,您的fizzbuz
函数要求传递给它的每个闭包都是相同的类型F
。如果您将fizzbuzz
的签名更改为:
fn fizzbuzz<F: Fn(i64) -> bool, G: Fn(i64) -> bool>(n: i64, f: F, fs: &str, b: G, bs: &str)
您的代码将进行类型检查。
基本上,<F: Fn(i64) -> bool>
语法不会为实现trait参数(Fn(i64) -> bool
)的类型创建通配符,但声明一个必须满足trait参数并且必须相同的类型在任何地方都使用它。未装箱的闭包定义必须是不同的类型,因为它可以包装不同的环境,但也因为它们分派到不同的功能(也就是说,每个具有不同的主体)。因此,fizzbuzz
需要两个不同的类型参数来容纳两种闭包类型。
答案 1 :(得分:4)
这段代码展示了问题的实质:
fn show_both<S: Show>(x: S, y: S) {
println!("{} {}", x, y);
}
您只能使用相同类型的两个参数来调用它,即允许这样做:
let x: i32 = 10;
let y: i32 = 20;
show_both(x, y);
但这不是:
let x: i32 = 10;
let y: f64 = 20.0;
show_both(x, y);
这很自然:您指定两个参数必须属于同一类型,即使此类型可以是任意的,只要它实现Show
。
你的代码基本上是相同的东西:
fn fizzbuzz<F: Fn(i64) -> bool>(n: i64, f: F, fs: &str, b: F, bs: &str)
您声明f
和b
必须具有相同的类型。但是,对于每个未装箱的闭包,编译器会生成不同的类型 - 这也很自然,因为不同的闭包可以捕获不同的变量。
您需要指定不同的类型参数才能传递不同的闭包:
fn fizzbuzz<F1, F2>(n: i64, f: F1, fs: &str, b: F2, bs: &str)
where F1: Fn(i64) -> bool,
F2: Fn(i64) -> bool