0

Ways that make recursive closure are not suitable for me from the accepted answer of the question:

Is it possible to make a recursive closure in Rust?

My closure need to be returned from a function and need to move variable from the environment into it, and can mutate it.

Then I find a way seems more suitable for me:

Anonymous recursion with closures

use std::cell::RefCell;

fn main() {
    let id = &(|a| a) as &Fn(u64) -> u64;
    let (fib, fib_p): (_, RefCell<&Fn(u64) -> u64>);
    fib_p = RefCell::new(id);

    fib = |n: u64| {
        if n < 2 {
            n
        } else {
            (fib_p.borrow())(n - 2) + (fib_p.borrow())(n - 1)
        }
    };

    *fib_p.borrow_mut() = &fib;

    println!("{}", fib(10));        
}

The above code works fine.

But my closure need to be returned from a function, so it can't be a reference to prevent dangling Reference, and we don't know the size of the closure from compile time, so I used the smart pointer Box for it. The below code throws an error:

use std::cell::RefCell;

fn main() {
    let id: Box<Fn(u64) -> u64> = Box::new(|a| a);
    let (fib, fib_p): (Box<Fn(u64) -> u64>, RefCell<&Box<Fn(u64) -> u64>>);
    fib_p = RefCell::new(&id);

    fib = Box::new(|n: u64| {
        if n < 2 {
            n
        } else {
            (fib_p.borrow())(n - 2) + (fib_p.borrow())(n - 1)
        }
    });

    *fib_p.borrow_mut() = &fib;

    println!("{}", fib(10));        
}
error[E0597]: `fib_p` does not live long enough
  --> src/main.rs:12:15
   |
8  |     fib = Box::new(|n: u64| {
   |                    -------- capture occurs here
...
12 |             (&fib_p.borrow())(n - 2) + (&fib_p.borrow())(n - 1)
   |               ^^^^^ borrowed value does not live long enough
...
19 | }
   | - borrowed value dropped before borrower
   |
   = note: values in a scope are dropped in the opposite order they are created
周汉成
  • 1,217
  • 2
  • 13
  • 24
  • “My closure need to be returned from a function and need to capture variable from the environment.” — those two requirements are mutually incompatible, because the environment does not live long enough. You have to wrap the environment in something that will be part of the returned function whether it is another closure or an explicit struct implementing `Fn`. – Jan Hudec Oct 02 '18 at 07:00
  • @JanHudec No, just move the ownership of variable from environmental to the closure use `move | | {}` syntax, then it works. – 周汉成 Oct 02 '18 at 10:04
  • But than it is not captured from the environment, but moved into the closure. That is different thing. – Jan Hudec Oct 02 '18 at 10:24

1 Answers1

1

You can wrap your recursive context in a non recursive closure:

pub fn fib() -> Box<Fn(u64) -> u64> {
    Box::new(|n: u64| {
        let id = &(|a| a) as &Fn(u64) -> u64;
        let (fib, fib_p): (_, RefCell<&Fn(u64) -> u64>);
        fib_p = RefCell::new(id);

        fib = |n: u64| {
            if n < 2 {
                n
            } else {
                (fib_p.borrow())(n - 2) + (fib_p.borrow())(n - 1)
            }
        };

        *fib_p.borrow_mut() = &fib;

        fib(n)
    })
}
Guillaume Gris
  • 2,135
  • 17
  • 34
  • My bad, I missed some details.The closure need to move the environmental variable, and can mutate it, I use your code for that situation, it doesn't work. Check https://gist.github.com/rust-play/8a89589562927ecf398494f6b6f67c4e, – 周汉成 Oct 02 '18 at 11:00