Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Cannot move out of X, a captured variable in an `FnMut` closure

I implemented a trait to share similar logic between my structs. This trait provides a collect_responses function, which takes vector of handlers and return vector of responses from the handlers. But after I applied this trait I got an errors like:

error[E0507]: cannot move out of `input`, a captured variable in an `FnMut` closure
  --> src/lib.rs:16:30
   |
12 |         input: HandlerInput
   |         ----- captured outer variable
...
16 |             .map(|func| func(input))
   |                  ------------^^^^^-
   |                  |           |
   |                  |           move occurs because `input` has type `HandlerInput<'_>`, which does not implement the `Copy` trait
   |                  captured by this `FnMut` closure

error[E0382]: use of partially moved value: `input`

This is my trait code:

pub trait Processor {
    fn process_input(input: HandlerInput) -> Result<Vec<HandlerOutput>, Error>;

    fn collect_responses(
        handlers: Vec<fn(HandlerInput) -> Result<HandlerOutput, Error>>,
        input: HandlerInput
    ) -> Result<Vec<HandlerOutput>, Error> {
        let responses = handlers
            .iter()
            .map(|func| func(input))
            .filter(|r| match r {
                Ok(_) => true,
                _ => false,
            })
            .map(|r| r.unwrap())
            .collect::<Vec<HandlerOutput>>();

        return if responses.is_empty() {
            Err(
                Error::new(
                    ErrorKind::Other,
                    "No successful response"
                )
            )
        } else {
            Ok(responses)
        }
    }
}

This is sandbox implementation in context of my real code.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

Could somebody explain, why this issue happen and how to fix it ?

>Solution :

fn(HandlerInput) -> Result<HandlerOutput, Error>

This is almost certainly not the type you want, for multiple reasons. First, fn is a function pointer, i.e. a literal function written at the top level in your application. It can’t be a closure or some other function-like thing. fn is almost exclusively used for interfacing with C, so you, at minimum, want Fn, FnMut, or FnOnce. See that link for more details on the difference. FnOnce won’t suffice because you need to call it in a map, so I’m guessing you want FnMut, but read the difference between FnMut and Fn and determine for yourself which one you need.

Second, if you write

FnMut(HandlerInput) -> Result<HandlerOutput, Error>

This is a function that takes a HandlerInput by value. It moves its argument and the caller can never use the argument again. So you certainly can’t call it several times on the same handler in a .map, since the very first call moves the value and renders the variable handler unusable. If HandlerInput implements Clone, then you can write

.map(|func| func(input.clone())

But that’s probably not what you want. Almost certainly, you want a reference here.

FnMut(&HandlerInput) -> Result<HandlerOutput, Error>
FnMut(&mut HandlerInput) -> Result<HandlerOutput, Error>

The first is a function which takes an immutable reference to a HandlerInput. It can read the HandlerInput but can’t change, move, or drop it. If HandlerInput is a read-only data structure, then this is the best option.

The second is a function which takes a mutable reference. It can read and modify the HandlerInput but can’t move or drop it. It also can’t be safely used across threads. Use this if your handlers are intended to modify the input argument.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading