This is of course a simplified version of my actual problem.
fn my_func<'a, 'b>(a: &'a i32, b: &'b i32) -> i32 {
a + b
}
fn my_func_generic<'a, 'b, T>(a: &'a T, b: &'b T) -> T
where
&'a T: std::ops::Add<&'b T, Output = T>,
{
a + b
}
fn apply_function(x: &i32, y: &i32, function: fn(&i32, &i32) -> i32) -> i32 {
function(x, y)
}
fn main() {
let x = 10;
let y = 11;
println!("{}", my_func(&x, &y));
println!("{}", my_func_generic(&x, &y));
println!("{}", apply_function(&x, &y, my_func));
println!("{}", apply_function(&x, &y, my_func_generic));
}
error[E0308]: mismatched types
--> src/main.rs:23:43
|
23 | println!("{}", apply_function(&x, &y, my_func_generic));
| ^^^^^^^^^^^^^^^ one type is more general than the other
|
= note: expected fn pointer `for<'r, 's> fn(&'r i32, &'s i32) -> _`
found fn pointer `fn(&i32, &i32) -> _`
The question I have is: How can I use my_func_generic as a function pointer to pass it into apply_function?
It can definitely be used as such a function, as println!("{}", my_func_generic(&x, &y)) demonstrates.
Trying to write it in a pointer gives the same error:
let function_pointer: fn(&i32, &i32) -> i32 = my_func_generic;
Annotating generic types manually gives the same error:
let function_pointer: fn(&i32, &i32) -> i32 = my_func_generic::<'_, '_, i32>;
Sadly I don't understand the error message, and don't have enough knowledge about the inner type system of Rust generics to understand how I could solve this issue, or if it's simply not possible due to some mechanism yet unknown to me.
TL;DR: In your case, you can do it as follows:
fn apply_function<'a, 'b>(x: &'a i32, y: &'b i32, function: fn(&'a i32, &'b i32) -> i32) -> i32
But in general, this is impossible.
There are two kinds of lifetimes in Rust: late-bound and early-bound. The difference between the two boils to when we bind the generic lifetime to a concrete lifetime.
For early-bound lifetime, we bind them when you're referring to their function. For late-bound lifetimes, referring to their function gives you a function pointer (or item) generic over the lifetime - what we call HRTB (for<'lifetime> fn(...)), and we choose the actual lifetime only when you call them.
The important point is that any constraints on the lifetime turns it into an early bound lifetime. This is because it is generally impossible to express constrained lifetimes as HRTB (more details on the above linked rustc-dev-guide page), although to be honest I still don't fully understand the reason.
So in my_func_generic(), 'a and 'b are early bound. However, apply_function() expects them to be late-bound (since the default desugaring for elided lifetimes in function pointers is HRTB, fn(&i32, &i32) -> i32 is actually for<'a, 'b> fn(&'a i32, &'b i32) -> i32), and so this is a type mismatch.
In this case, you can just change apply_function() to bind the lifetimes as parameters, as I suggested above. This will still accept late-bound lifetimes, as we'll just bind them immediately. However, as far as I know, in general it is impossible to convert an early-bound lifetime to a late-bound one.
It works if you change your trait bounds slightly:
fn my_func_generic<'a, 'b, T>(a: &'a T, b: &'b T) -> T
where
T: Copy + std::ops::Add<Output = T>,
{
*a + *b
}
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