1
fn main() {
    let mut a = String::from("dd");
    let mut x = move || {
        a.push_str("string: &str");
    };
    x();
    x();
}

I have added move here to capture a but I am still able to call the x closure twice. Is a still borrowed as a mutable reference here? Why doesn't move force a move?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
raj
  • 5,989
  • 7
  • 30
  • 62

2 Answers2

3

The variable a has indeed been moved into the closure:

fn main() {
    let mut a = String::from("dd");
    let mut x = move || {
        a.push_str("string: &str");
    };
    x();
    x();

    a.len();
}
error[E0382]: borrow of moved value: `a`
 --> src/main.rs:9:5
  |
2 |     let mut a = String::from("dd");
  |         ----- move occurs because `a` has type `std::string::String`, which does not implement the `Copy` trait
3 |     let mut x = move || {
  |                 ------- value moved into closure here
4 |         a.push_str("string: &str");
  |         - variable moved due to use in closure
...
9 |     a.len();
  |     ^ value borrowed here after move

It's unclear why you think that the closure x would become invalid after calling it, but it doesn't. No more than the same applied to a struct:

struct ClosureLike {
    a: String,
}

impl ClosureLike {
    fn call(&mut self) {
        self.a.push_str("string: &str");
    }
}

fn main() {
    let a = String::from("dd");
    let mut x = ClosureLike { a };
    x.call();
    x.call();
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • The value `a` has been moved to the closure. So shouldnt `a` be dropped at the end of the closure call ? Isnt that the reason why `FnOnce` can only be called once? And an equivalent `move` `ClosureLike` implementation should have ` fn call(mut self) {` in it right to move `self`not borrow. – raj Aug 13 '19 at 02:47
  • @raj no, it will be dropped when the closure is dropped. The closure here implements `FnMut`; it can be called multiple times. See [when does a closure implement Fn, FnMut, and FnOnce?](https://stackoverflow.com/q/30177395/155423) – Shepmaster Aug 13 '19 at 02:51
  • @raj keeping rest of the semantics same, here's the `FnOnce` [version of your code](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=9382cd11d99a7ae1a22d765a814920ac) . The error is self explanatory. – vikram2784 Aug 13 '19 at 05:53
0

The question came from my wrong understanding of closures. The way it is documented in the Rust book also contributed to the confusion (I am not saying the book is bad). If anyone else had this same confusion, here is what I found.

Closures do not just store the scope and run it when its called. It captures the environment in the preferred way. The environment which contains a is stored in the closure. How the values are captured from the environment decides the trait.

The value of a persists until the closure exists, unless some operation moves it, such as if the closure returns a or a method consumes a. Here, nothing moves a out of the closure so the closure can be called as many times as I want.

A better understanding can be obtained from the FnOnce, FnMut, and Fn traits. These traits are decided by how the variables are captured by the closure, not by how the variables are moved into the closure. FnMut can be implemented on a closure where a value is moved .

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
raj
  • 5,989
  • 7
  • 30
  • 62