4

I am playing with examples from Understanding Closures in Rust.. I defined the following structure:

struct MyStruct {
    text: &'static str,
    number: u32,
}

impl MyStruct {
    fn new(text: &'static str, number: u32) -> MyStruct {
        MyStruct {
            text: text,
            number: number,
        }
    }
    // We have to specify that 'self' is an argument.
    fn get_number(&self) -> u32 {
        self.number
    }
    // We can specify different kinds of ownership and mutability of self.
    fn inc_number(&mut self) {
        self.number += 1;
    }
    // There are three different types of 'self'
    fn destructor(self) {
        println!("Destructing {}", self.text);
    }
}

I created these "testing" functions to classify a closure based on compile time errors:

fn is_fn<A, R>(_x: fn(A) -> R) {}
fn is_Fn<A, R, F: Fn(A) -> R>(_x: &F) {}
fn is_FnMut<A, R, F: FnMut(A) -> R>(_x: &F) {}
fn is_FnOnce<A, R, F: FnOnce(A) -> R>(_x: &F) {}

The article demonstrates following example as a FnOnce closure:

let obj1 = MyStruct::new("Hello", 15);
let obj2 = MyStruct::new("More Text", 10);
// obj1 is owned by the closure
let mut closure4 = move |x: &MyStruct| {
    obj1.destructor();
    x.get_number()
};
is_FnMut(&closure4);

This has a compilation error:

error[E0525]: expected a closure that implements the `FnMut` trait, but this closure only implements `FnOnce`
  --> src/main.rs:36:24
   |
36 |     let mut closure4 = move |x: &MyStruct| {
   |                        ^^^^^^^^^^^^^^^^^^^ this closure implements `FnOnce`, not `FnMut`
37 |         obj1.destructor();
   |         ---- closure is `FnOnce` because it moves the variable `obj1` out of its environment
...
40 |     is_FnMut(&closure4);
   |     -------- the requirement to implement `FnMut` derives from here

That makes sense. From my understanding, the compiler infers that the value is moved into the closure because the destructor is called.

I was trying to see what else causes the closure to be inferred as FnOnce. I declared the closure as a move closure:

let obj1 = MyStruct::new("Hello", 15);
let obj2 = MyStruct::new("More Text", 10);
// obj1 is owned by the closure
let mut closure4 = move |x: &MyStruct| {
    obj1.get_number();
    x.get_number()
};
is_Fn(&closure4);
is_FnMut(&closure4);

To my surprise, this actually passes, so this closure is actually considered to be also Fn, not exclusively FnOnce as I expected. Why is that? I thought if a value is moved into closure, it wouldn't implement Fn but only FnOnce.

So I though, "Okay, maybe somehow obj1 is not really moved into the closure, let me test that". So I tried to use obj1 in any way after constructing closure4, the compiler indicates that obj1 was indeed moved into the closure:

let obj1 = MyStruct::new("Hello", 15);
let obj2 = MyStruct::new("More Text", 10);
// obj1 is owned by the closure
let mut closure4 = move |x: &MyStruct| {
    obj1.get_number();
    x.get_number()
};
obj1.get_number();
error[E0382]: borrow of moved value: `obj1`
  --> src/main.rs:40:5
   |
33 |     let obj1 = MyStruct::new("Hello", 15);
   |         ---- move occurs because `obj1` has type `MyStruct`, which does not implement the `Copy` trait
...
36 |     let mut closure4 = move |x: &MyStruct| {
   |                        ------------------- value moved into closure here
37 |         obj1.get_number();
   |         ---- variable moved due to use in closure
...
40 |     obj1.get_number();
   |     ^^^^ value borrowed here after move

Can someone enlighten this behaviour for me? What must hold true for a closure to implement none of fn, Fn, FnMut, but only implement FnOnce?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Patrik Stas
  • 1,883
  • 17
  • 24
  • 1
    TL;DR the duplicates: the **body** of the closure determines what traits are implemented. Your closure only takes references to the value, so it can be called multiple times. – Shepmaster Oct 23 '19 at 16:14

0 Answers0