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?
C++ Generic lambdas. Example. c++14. Lambda functions can take arguments of arbitrary types. This allows a lambda to be more generic: auto twice = [](auto x){ return x+x; }; int i = twice(2); // i == 4 std::string s = twice("hello"); // s == "hellohello".
The syntax for lambdas is one of the weirder things in C++, and takes a bit of getting used to. Lambdas take the form: The capture clause and parameters can both be empty if they are not needed. The return type is optional, and if omitted, auto will be assumed (thus using type inference used to determine the return type).
If return type inference is used, all return statements in the lambda must return the same type (otherwise the compiler won’t know which one to prefer). This produces a compile error because the return type of the first return statement (int) doesn’t match the return type of the second return statement (double).
The lambda has no capture clause (we’ll explain what a capture clause is in the next lesson) because it doesn’t need one. And we’ve omitted the trailing return type in the lambda (for conciseness), but since operator!= returns a bool, our lambda will return a bool too. In the above example, we defined a lambda right where it was needed.
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
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With