1

I'm trying to pass a particular closure to a function in an external crate that requires a &impl Fn, and will store a reference internally.

I've gotten my problem simplified to to look like this:

use std::rc::Rc;
use std::cell::Cell;

/// stuff I can control

struct MyWrapper<'a> {
    obj: Option<LibObj<'a, f64>>,
    injection_closure: Box<dyn Fn(&f64) -> f64 + 'a >,
}

impl<'a> MyWrapper<'a> {
    fn new(transaction: Rc<Cell<f64>>) -> MyWrapper<'a> {
    // I want any way to get this closure stuffed into a LibObj
    // doesn't matter if it's stored in a Box, Rc, Mutex, ...
        let injection_closure = Box::new({ move |_: &f64| transaction.get() });
        let mut retv = MyWrapper {
            injection_closure: injection_closure,
            obj: None,
        };
        retv.obj = Some(LibObj::new(
            &*retv.injection_closure
        ));

        retv
    }
}


/// stuff I can't control

struct LibObj<'f_lifetime, Arg> {
    my_thing: ThingWrapper<'f_lifetime, Arg>,
}

impl<'f_lifetime, Arg> LibObj<'f_lifetime, Arg>
{
    pub fn new(f: &'f_lifetime impl Fn(&Arg) -> Arg)
               -> Self
    {
        let thing = ThingWrapper::new(f);
        LibObj { my_thing: thing }
    }
}

pub struct ThingWrapper<'f_lifetime, Arg>
{
    f: &'f_lifetime dyn Fn(&Arg) -> Arg,
}

impl<'f_lifetime, Arg> ThingWrapper<'f_lifetime, Arg>
{
    pub fn new(f: &'f_lifetime impl Fn(&Arg) -> Arg)
               -> Self
    {
        ThingWrapper { f }
    }
}

This solution doesn't compile, giving the error error[E0277]: the size for values of type 'dyn for<'r> Fn(&'r f64) -> f64' cannot be known at compilation time. I understand what the error means and why it says as much, but I'm having trouble figuring out how to avoid it while still respecting the necessary lifetime constraints.

Sample code is on Rust Playground at here.

deftfyodor
  • 294
  • 1
  • 9
  • 1
    I'm afraid this is unsolvable the way it is shown in your code. LibObj contains the lifetime of the Fn reference, because it does not own it. So no matter what you do, you need to keep `injection_closure` alife while the `LibObj` object exists. Which obviously isn't the case if you leave the function and store it in the return value. – Finomnis Aug 31 '22 at 15:05
  • I know that you are trying to keep it alive by storing it in the other object, but that sadly makes it a duplicate of another impossible problem. – Finomnis Aug 31 '22 at 15:06
  • So while storing it in a Box makes this technically sound, I don't think you will be able to implement it without advanced constructs like `unsafe` and `Pin`. – Finomnis Aug 31 '22 at 15:13
  • I think using `Pin` is a good idea I hadn't thought of. I'll try that out. – deftfyodor Aug 31 '22 at 15:17
  • 1
    `Pin` is just a helper in conjunction with `unsafe` and raw pointers to create sound code. Just fyi. – Finomnis Aug 31 '22 at 15:20
  • Here is a really long article I wrote about `Pin` & `Box`, in case it helps: https://stackoverflow.com/questions/72516441/pin-vs-box-why-is-box-not-enough – Finomnis Aug 31 '22 at 15:23

0 Answers0