According to the article, a closure means that the compiler generates a structure and then implements the three features of `Fn, FnMut and FnOnce`. Is there a way to know which characteristic the compiler recognizes for the defined closure?

fn main() {
    let mut x = 0;
    let incr_x = move || {
        x += 1;
        println!("x: {}", x);
    };

For example, in the code above, I think that incr_x is a FnMut trait, but I'm still not sure. Is there a way to check what traits the compiler finally recognizes?

According to what kind of rules does the compiler execute when the closure is executed, which trait is executed?
If there is a link to the existing information, please send it to me, thank you all

Usually you'd just use it, and see if it worked, e.g.:

fn is_it<T: FnMut()>(_arg: T) {}
is_it(incr_x);

I don't think you can observe the difference between various function traits from the closure's perspective, so it shouldn't matter. It implements all traits that are compatible with it, and executes the same for all of them.

1 Like

I'm not sure which article you're referring to, so in case it didn't mention it -- FnMut closures also implement FnOnce, and Fn closures also implement both FnMut and FnOnce. These relationships are expressed as sub/supertrait relationships. The closures may also be Copy, Clone, Send, and Sync depending on what is captured.

More documentation here and here.

In practice, when you have a function that takes a closure, you'll bind the argument on one the traits -- usually the least "flexible" version you need (FnOnce, FnMut, or Fn) in order to accept the most closures. Usually this is the only thing you need to think about, and it will just work. Sometimes you need to add extra bounds, like + Send (if you're sending it to another thread, say).

When you call the closure, the most flexible version will be used -- so if you bind on FnMut, you'll call FnMut, not FnOnce. (FnOnce is still available because of the supertrait relationship. This can matter if you need to pass the closure on to something with a different bound. But again you'll almost never need to think about it.) The difference between Fn and FnMut only matters if you're extending the borrow of the closure by keeping a return value with a lifetime around.


And now for a nit that you will probably never run into.

When executed in different contexts, the body executes the same, but if you call something only known (e.g. by trait bounds) to be a FnOnce, it will consume the closure. If you try to use the closure after that, it will either fail or -- if the closure is (known to be) Copy -- execute a copy of the closure. This latter case may be surprising in the case of closure with state.

You'll almost surely never run into this directly because you'll almost surely never write a FnOnce + Copy bound.

However, a similar situation can happen when you create a closure and pass it to a function that takes the closure by value -- you may unwittingly pass a copy of the closure in some cases.

Example.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.