Skip to content

Closure moved out inside own argument can be reborrowed after moving #17144

Closed
@huonw

Description

@huonw
fn call_rec(n: uint, f: |uint| -> uint) -> uint {
    if n == 0 { return 1 }

    f(call_rec(n - 1, f))                       // A
    // let tmp = call_rec(n - 1, f); f(tmp)        // B
    // (|x| f(x))(call_rec(n - 1, f))              // C
    // let tmp = call_rec(n - 1, |x| f(x)); f(tmp) // D
}

fn main() {
    let mut y = 0u;
    let f = |x| { y += x; y };

    println!("{}", call_rec(5, f));
}

A

("Obvious" code.)

<anon>:4:23: 4:24 error: cannot move out of `f` because it is borrowed
<anon>:4     f(call_rec(n - 1, f)) // A
                               ^
<anon>:4:5: 4:6 note: borrow of `f` occurs here
<anon>:4     f(call_rec(n - 1, f)) // A
             ^

B

(Use a temporary.)

<anon>:5:35: 5:36 error: use of moved value: `f`
<anon>:5     let tmp = call_rec(n - 1, f); f(tmp) // B
                                           ^
<anon>:5:31: 5:32 note: `f` moved here because it has type `|uint| -> uint`, which is a non-copyable stack closure (capture it in a new closure, e.g. `|x| f(x)`, to override)
<anon>:5     let tmp = call_rec(n - 1, f); f(tmp) // B
                                       ^

C

(Reborrow f for the outer call.)

Incorrectly compiles & runs.

D

(Reborrow f for the inner call.)

Correctly compiles & runs.


It seems that A, B and C should all be essentially equivalent, and thus should all not compile, because f is moved into the recursive call before the outer call (and hence that outer call should be disallowed), and thus the reborrowing in C should be illegal (it is reborrowing the moved-from f).

This may just disappear once these closures are removed.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions