Any way to return an closure that would returns a reference to one of its captured variable?

Hi there,

I am currently struggling with a code that returning a closure. What I am trying to do describes as following.

fn init_work(param:Param) -> impl Fn() -> MyResult;

What I want to implement is make the function init_work do some initialization works. And then move all the initialized states into the closure and return.

On the call site of init_work, what I want to do is

let get_result = init_work(param);
// And then I can call get_result multiple times.
let actual_result_1 = get_result();
// .......
let actual_result_2 = get_result();
......

It works if the MyResult doesn't have any reference to the closure. Unfortunately, I have to make reference to the closure to make a MyResult type. So I have to put some lifetime param to MyResult

fn init_work(param:Param) -> impl Fn() -> MyResult<'???>;

It seems reasonable to let MyResult has a lifetime parameter that doesn't outlive the lifetime of closure itself.
Each time the closure is called, it borrows a reference from the closure to the caller. And if the programmer try to drop the closure before all the result are drop, the compiler should be able to detect the error.

However, I don't know how to declare the closure's return value to have the lifetime of the closure itself.

If I have an object, it's quite easy to write the following code.

struct Context { ..... }
impl Context {
    fn get_result<'a>(&'a self) -> MyResult<'a>; 
}
fn init_work(param:Param) -> Context { ... }

let ctx = init_work(param);
let result = ctx.get_result();

However, I don't know how to do the same thing with a closure.

--- Update ---

I know the context struct is a work around. But my case is that I have too many states to capture and that is why I want a closure which can automatically hold the ownership of the states that would be used later.

So what I am asking is if there's an solution that I can implement the similar thing with closure without actually making a big context struct.


So I am wondering is there any workaround ?

Sorry, no, I don't think this can work.

The calling traits are defined like so:

pub trait FnOnce<Args> {
    type Output;
    extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
}
pub trait FnMut<Args>: FnOnce<Args> {
    extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
}
pub trait Fn<Args>: FnMut<Args> {
    extern "rust-call" fn call(&self, args: Args) -> Self::Output;
}

Since Output has no lifetime parameter, there's no way to tie that to &self.

When we get generic associated types, it would be possible to write a trait with something like type Output<'a>. I'm not sure if it's possible to extend the Fn traits that way though.

1 Like

Your struct Context is the workaround. That or a trait that ties the lifetime of Output to the borrow of the call to the closure.

trait BorrowingFn<'a> {
    type Output;

    fn call(&'a self) -> Self::Output;
}

// accepting one as an argument
fn foo(f: impl for<'a> BorrowingFn<'a>) {
    let x = f.call();
    unimplemented!()
}

// returning one from a function
fn bar() -> impl for<'a> BorrowingFn<'a> {
    struct MyClosure(u32);
    impl<'a> BorrowingFn<'a> for MyClosure {
        type Output = &'a u32;
        fn call(&'a self) -> Self::Output { &self.0 }
    }
    
    MyClosure(3)
}
1 Like

I see, it seems we also cannot define an output type with a lifetime parameter of self, since the associated type is defined without self.

It seems I need a High Kinded Types for this case.

It sounds like I need to take that context struct workaround. But that one is really messy in my case :frowning:

I think once those feature is supported, I think it's possible to do this for sure with

type Output<'a> = MyResult<'a>;
extern "rust-call" fn call(&'self  self, args: Args) -> Self::Output<'self>;

And there should be a syntax sugar for the self lifetime parameter.

That should work pretty well.

But what I want to do this avoid making a context struct or move the state manually.
And I think since the context struct is doable, so what I want with closure should be well-defined.

I am not sure if there’s anyway to do that. But it would be nice if the compiler can automatically move those value.

Given you’re returning a holder of the values (ie MyResult), I’m not sure what the closure is really buying you? If MyResult is a tuple struct, then “moving values” into it is dirt simple in terms of ergonomics (and even if it’s a normal struct, it’s still ergonomic). What am I missing?

Yes, if MyResult can be constructed by simply moving values into it, that would be really simple. But if MyResult has to carries a reference to the internal data (for example MyResult is an iterator of which items has a field points to one of shared metadata hold by the closure), this is not trivial then.