Reputation: 2198
I have this code (contrived and purely experimental)
fn math_op(op: &str) -> Option<Box<Fn(i32, i32) -> i32>> {
let ret: Option<Box<Fn(i32, i32) -> i32>> = match op {
"+" => Some(Box::new(|a: i32, b: i32| -> i32 { a + b } )),
"-" => Some(Box::new(|a: i32, b: i32| -> i32 { a - b } )),
"*" => Some(Box::new(|a: i32, b: i32| -> i32 { a * b } )),
"/" => Some(Box::new(|a: i32, b: i32| -> i32 { a / b } )),
_ => None,
};
ret
}
It returns a function/lambda that takes two operands and returns a result (in this case the addition, subtraction, divide and multiply operators)
Which can be called like so:
let add: Option<Box<Fn(i32, i32) -> i32>> = math_op("+");
println!("Add {}", add.unwrap()(10, 2));
I would really like to make a generic version of this, so far I have...
fn math_op_gen<T>(op: &str) -> Option<Box<Fn(T, T) -> T::Output>>
where T: std::ops::Add + std::ops::Sub + std::ops::Mul + std::ops::Div {
let ret: Option<Box<Fn(T, T) -> T::Output>> = match op {
"+" => Some(Box::new(|a, b| { a + b } )),
"-" => Some(Box::new(|a, b| { a - b } )),
"*" => Some(Box::new(|a, b| { a * b } )),
"/" => Some(Box::new(|a, b| { a / b } )),
_ => None,
};
ret
}
But when I build I get these errors:
error: ambiguous associated type `Output` in bounds of `T` [E0221]
note: associated type `T` could derive from `core::ops::Div`
note: associated type `T` could derive from `core::ops::Mul`
note: associated type `T` could derive from `core::ops::Sub`
note: associated type `T` could derive from `core::ops::Add`
I understand this is because the compiler cannot determine what type T::Output is from the various traits I have implemented. Is there another way of writing this to get it to work?
Upvotes: 5
Views: 1201
Reputation: 65682
You need the output types of Add
, Sub
, Mul
and Div
to be the same. You can enforce this by adding another type parameter and constraining each trait's Output
to be this type parameter.
fn math_op_gen<T, R>(op: &str) -> Option<Box<Fn(T, T) -> R>>
where T: std::ops::Add<Output=R> +
std::ops::Sub<Output=R> +
std::ops::Mul<Output=R> +
std::ops::Div<Output=R> {
let ret: Option<Box<Fn(T, T) -> R>> = match op {
"+" => Some(Box::new(|a, b| { a + b } )),
"-" => Some(Box::new(|a, b| { a - b } )),
"*" => Some(Box::new(|a, b| { a * b } )),
"/" => Some(Box::new(|a, b| { a / b } )),
_ => None,
};
ret
}
Upvotes: 6