Rust trait that accepts (optionally) referencial fn parameter

Viewed 58

For reasons that are not particularly important in this context, I want to be able to do something very similar to

pub trait MapValue<Out> {
    fn map_value<C>(&self, map_func: fn(C, Out), context: C);
}

impl MapValue<i32> for i32 {
    fn map_value<C>(&self, map_func: fn(C, i32), context: C) {
        map_func(context, *self)
    }
}

impl MapValue<&i32> for i32 {
    fn map_value<C>(&self, map_func: fn(C, &i32), context: C) {
        map_func(context, self)
    }
}

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=12afa185d3bf8518320f0fd7862aeb79

for various different types. Unfortunately that fails to compile with

   = note: expected fn pointer `fn(&i32, fn(_, &i32), _)`
              found fn pointer `fn(&i32, for<'r> fn(_, &'r i32), _)`

If I try to make the lifetime of the i32 explicit as in

impl<'a> MapValue<&'a i32> for i32 {
    fn map_value<C>(&'a self, map_func: fn(C, &'a i32), context: C) {
        map_func(context, self)
    }
}

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=f17b29450683c11093315eb17199504d

then it fails with

note: the anonymous lifetime #1 defined on the method body at 12:5...
  --> src/main.rs:12:5
   |
12 |     fn map_value<C>(&'a self, map_func: fn(C, &'a i32), context: C) {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...does not necessarily outlive the lifetime `'a` as defined on the impl at 11:6

Obviously I must be doing my lifetime annotations wrong as the call to map_func returns before map_value so it should be impossible that self does not live long enough to be passed into map_func. Any ideas?

EDIT:

I am free to edit the trait and the solution posted by user4815162342 at https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=d5763635625281acc066527207b290a7 to switch to

pub trait MapValue<'a, Out> {
    fn map_value<C>(&'a self, map_func: fn(C, Out), context: C);
}

does indeed work for the problem that I posted. Curiously it doesn't work in all cases where I use it though. I also have a situation similar to

impl<'a, 'b: 'a> MapValue<'b, &'a i32> for i32 {
    fn map_value<C>(&'b self, map_func: fn(C, &'a i32), context: C) {
        let cpy = *self; // Something more useful happens here in the real code
        map_func(context, &cpy);
    }
}

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=e0790588121ad569781b2ddfc9d33ee9

that fails with the following error

14 |         map_func(context, &cpy);
   |         ------------------^^^^-
   |         |                 |
   |         |                 borrowed value does not live long enough
   |         argument requires that `cpy` is borrowed for `'a`
15 |     }
   |     - `cpy` dropped here while still borrowed

even with the modified trait. I didn't originally post it as I thought it had the same underlying issue as my original second case, but obviously not. Again, I don't see why there would be an issue because the function returns before the variable goes out of scope.

0 Answers
Related