The error here is extremely unclear to me:
use std::env;
fn main() {
let args: Vec<_> = env::args().collect();
let op = match &args[1][..] {
"foo" => |c: i32| c + 1,
"bar" => |c: i32| c + 1,
"baz" => |c: i32| c - 1,
"other" => |c: i32| c - 1,
_ => return,
};
println!("{:?}", op(11));
}
Not sure if actually a bug in rust (That's my intuition, this looks very valid to me) or just an issue where the error message is incredibly unhelpful.
<anon>:5:14: 11:6 error: match arms have incompatible types:
expected `[closure <anon>:6:18: 6:32]`,
found `[closure <anon>:7:18: 7:32]`
(expected closure,
found a different closure) [E0308]
<anon>:5 let op = match &args[1][..] {
<anon>:6 "foo" => |c: i32| c + 1,
<anon>:7 "bar" => |c: i32| c + 1,
<anon>:8 "baz" => |c: i32| c - 1,
<anon>:9 "other" => |c: i32| c - 1,
<anon>:10 _ => return,
...
<anon>:7:18: 7:32 note: match arm with an incompatible type
<anon>:7 "bar" => |c: i32| c + 1,
^~~~~~~~~~~~~~
error: aborting due to previous error
The error is a bit confusing if you're not familiar with closures. Check out the chapters on Closures and Static and Dynamic Dispatch in the book to understand the semantics and performance tradeoffs.
Currently in Rust, no two closures, even if identical, have the same type. Closures are essentially anonymous structs (containing the environment they close over) expanded at compile time, and made to impl the Fn* family of traits.
In your code, producing a bare closure from a match arm that could produce other closures is not possible. You must use trait objects. The easiest approach would probably be to just box each closure, and coerce it into a trait object with a type annotation.
use std::env;
fn main() {
let args: Vec<_> = env::args().collect();
let op: Box<Fn(i32) -> i32> = match &args[1][..] {
"foo" => Box::new(|c: i32| c + 1),
"bar" => Box::new(|c: i32| c + 1),
"baz" => Box::new(|c: i32| c - 1),
"other" => Box::new(|c: i32| c - 1),
_ => return,
};
println!("{:?}", op(11));
}
There may be other ways to accomplish what you want. And if you aren't closing over your environment, just use function pointers.
use std::env;
fn first_thing(c: i32) -> i32 {
c + 1
}
fn second_thing(c: i32) -> i32 {
c - 1
}
fn main() {
let args: Vec<_> = env::args().collect();
let op: fn(i32) -> i32 = match &args[1][..] {
"foo" => first_thing,
"bar" => first_thing,
"baz" => second_thing,
"other" => second_thing,
_ => return,
};
println!("{:?}", op(11));
}
Or if your situation ultimately allows for it, use generics in some way.
Sorry, I forgot to mention that this is a regression from about a week ago when https://github.com/richo/karma-rs/blob/master/src/main.rs#L49-L55 built
On Friday, April 3, 2015, ebfull [email protected] wrote:
The error is a bit confusing if you're not familiar with closures. Check
out the chapters on Closures http://doc.rust-lang.org/book/closures.html
and Static and Dynamic Dispatch
http://doc.rust-lang.org/book/static-and-dynamic-dispatch.html in the
book to understand the semantics and performance tradeoffs.Currently in Rust, no two closures, even if identical, have the same type.
Closures are essentially anonymous structs (containing the environment they
close over) expanded at compile time, and made to impl the Fn* family of
traits.In your code, producing a bare closure from a match arm that could produce
other closures is not possible. You must use trait objects. The easiest
approach would probably be to just box each closure, and coerce it into a
trait object with a type annotation.use std::env;
fn main() {
let args: Vec<_> = env::args().collect();
let op: Boxi32> = match &args[1][..] {
"foo" => Box::new(|c: i32| c + 1),
"bar" => Box::new(|c: i32| c + 1),
"baz" => Box::new(|c: i32| c - 1),
"other" => Box::new(|c: i32| c - 1),
_ => return,
};
println!("{:?}", op(11));
}There may be other ways to accomplish what you want. And if you aren't
closing over your environment, just use function pointers.use std::env;
fn first_thing(c: i32) -> i32 {
c + 1
}
fn second_thing(c: i32) -> i32 {
c - 1
}
fn main() {
let args: Vec<_> = env::args().collect();
let op: fn(i32) -> i32 = match &args[1][..] {
"foo" => first_thing,
"bar" => first_thing,
"baz" => second_thing,
"other" => second_thing,
_ => return,
};
println!("{:?}", op(11));
}Or if your situation ultimately allows for it, use generics in some way.
—
Reply to this email directly or view it on GitHub
https://github.com/rust-lang/rust/issues/24036#issuecomment-89509870.
Closures in rust used to be "boxed" so they acted similar to my first example solution.
Great fix, and thanks for the explaination!
If you're satisfied the issue can be closed now.
Thanks!
Most helpful comment
The error is a bit confusing if you're not familiar with closures. Check out the chapters on Closures and Static and Dynamic Dispatch in the book to understand the semantics and performance tradeoffs.
Currently in Rust, no two closures, even if identical, have the same type. Closures are essentially anonymous structs (containing the environment they close over) expanded at compile time, and made to impl the Fn* family of traits.
In your code, producing a bare closure from a match arm that could produce other closures is not possible. You must use trait objects. The easiest approach would probably be to just box each closure, and coerce it into a trait object with a type annotation.
There may be other ways to accomplish what you want. And if you aren't closing over your environment, just use function pointers.
Or if your situation ultimately allows for it, use generics in some way.