30

Is there any way to create a local function like this Python code?

def h():
    final = []
    def a():
        for i in range(5):
            final.append(i)
        a()
        return final

I tried it, but failed:

fn h() -> Vec<i32> {
    let mut ff = vec![];
    fn a() {
        for i in 0..5 {
            ff.push(i)
        }
    };
    a();
    ff
}
 error[E0434]: can't capture dynamic environment in a fn item; use the || { ... } closure form instead
 --> src/main.rs:5:13
  |
5 |             ff.push(i)
  |             ^^
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Eva Red
  • 569
  • 1
  • 5
  • 11

1 Answers1

45

Functions in Rust don't capture variables from the surrounding environment, period. A "local" function in Rust is really just a global function that isn't globally visible; it can't do anything more than any other global function.

Instead, Rust has closures which are distinct from functions in that they do capture variables from their environment. That would look like this:

fn h() -> Vec<i32> {
    let mut ff = vec![];
    let mut a = || {
        for i in 0..5{
            ff.push(i)
        }
    };
    a();
    ff
}

Three things to note with this. Firstly, append is not what you want, you want push. You should check the documentation for Vec to see what's available and what methods do. Secondly, you have to make a mutable because it's mutating something it captured (see also this answer about Fn, FnMut, and FnOnce). Third, it won't compile:

error[E0505]: cannot move out of `ff` because it is borrowed
 --> <anon>:9:9
  |
3 |         let mut a = || {
  |                     -- borrow of `ff` occurs here
...
9 |         ff
  |         ^^ move out of `ff` occurs here

The problem is that by creating the closure, you had to give it a mutable borrow to ff. However, that borrow prevents anyone else from moving or otherwise messing with ff. You need to shorten the length of time this borrow exists:

fn h() -> Vec<i32> {
    let mut ff = vec![];
    {
        let mut a = || {
            for i in 0..5{
                ff.push(i)
            }
        };
        a();
    }
    ff
}

This works, but is kinda clunky. It's also unnecessary; the above could more cleanly be rewritten by just passing the borrow to ff into a local function explicitly:

fn h() -> Vec<i32> {
    let mut ff = vec![];
    fn a(ff: &mut Vec<i32>) {
        for i in 0..5{
            ff.push(i)
        }
    }
    a(&mut ff);
    ff
}

This last one is the best (if you're able to use it), because it keeps it clean when and why ff is being borrowed.

Community
  • 1
  • 1
DK.
  • 55,277
  • 5
  • 189
  • 162
  • I wonder if I can write a recursion in closure? If not, is there any way that ff is not the argument, but it mentioned in function a, and the function a is able to have recursion? – Eva Red Mar 20 '17 at 04:33
  • @EvaRed As I said, functions don't capture. *Period.* As for recursion, closures don't have names, so can't self recurse. Even if they could, I'm not sure it would work with the implicit borrowing going on. If you really, desperately *need* to do recursion with a closure, you can probably make it work (borrowing issues aside) using a trampoline, but that would be a different question. – DK. Mar 20 '17 at 04:37
  • 1
    @EvaRed I should probably note that a trampoline approach would likely end up being *significantly* more convoluted that just using a function and passing `&mut ff` explicitly. – DK. Mar 20 '17 at 04:48
  • Related: [Is it possible to make a recursive closure in Rust?](http://stackoverflow.com/q/16946888/155423) – Shepmaster Mar 28 '17 at 12:35