I have a "view" data structure in Rust that contains a reference to another data structure, and I want to automate testing of the getter methods that I implemented on the view data structure. Here's a MRE with a single getter:
/// View data structure.
struct View<'a> {
value: &'a u32,
}
impl<'a> View<'a> {
/// Getter method.
pub fn value(&self) -> u32 {
*self.value
}
}
/// Attempt to automate unit testing of the getter: create an underlying value,
/// then create a `View`, then apply the getter method to the `View` and ensure
/// we get the expected value out.
fn check_getter(getter: fn(&View) -> u32) {
let value: u32 = 7;
let view = View { value: &value };
assert!(getter(&view) == 7);
}
check_getter(View::value);
This fails to compile, with the following error:
mismatched types
expected fn pointer `for<'r, 's> fn(&'r tests::test_adcs::View<'s>) -> _`
found fn pointer `for<'r> fn(&'r tests::test_adcs::View<'_>) -> _`
I think I need to use HRTBs, and have tried various permutations, but haven't been able to get anything to compile.
TL;DR: Use a closure (you can use macros if you prefer):
check_getter(|v| v.value());
You fell into the trap of early-bound vs. late-bound lifetimes.
View::value
does not have the signature for<'a> fn <View<'a>>::value()
. Rather, it has the signature fn <View<'some_concrete_lifetime>>::value()
. That is, 'a
is not any lifetime, but some inferred lifetime. Therefore HRTB doesn't work - only one lifetime matches the given function. You can read more about early-bound and late-bound lifetimes in this recent answer I wrote, but the crux of the issue is that because the lifetime 'a
appears on the impl
, not the function itself, it becomes early-bound and has only one concrete lifetime for each instance of the function instead of being late-bound and allowed to use with any lifetime.
The fix is just to wrap it with a closure, making it late-bound.