This small FizzBuzz program using unboxed closures gives a rather mysterious error message.
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");
}
Error message:
<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
Could someone describe the error please? Thanks.
This piece of code demonstrates the essence of the problem:
You can only call it with both arguments of the same type, i.e. this is allowed:
But this is not:
This is fairly natural: you specified that both of the parameters must be of the same type, even though this type can be arbitrary as long as it implements
Show
.You have essentially the same thing in your code:
You declared that both
f
andb
must have the same type. However, for each unboxed closure the compiler generates a different type - this is quite natural as well, given that different closures can capture different variables.You need to specify different type parameters in order to be able to pass different closures:
Each unboxed closure definition creates a completely different type. This makes each of the closures defined in
main
a different type. Yourfizzbuz
function, on the other hand, requires each of the closures passed to it to be the same type,F
. If you changefizzbuzz
's signature to:your code will typecheck.
Basically, the
<F: Fn(i64) -> bool>
syntax doesn't create a wildcard for types that implement the trait parameter (Fn(i64) -> bool
), but declares a single type which must satisfy the trait parameter and be the same type everywhere it is used. Unboxed closure definitions must be different types both because the may wrap different environments but also because they dispatch to different functions (that is, each has a different body). As a result,fizzbuzz
needs two different type parameters to accommodate the two closure types.