2

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.

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

Sergio Ivanuzzo
  • 1,820
  • 4
  • 29
  • 59

1 Answers1

3
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.

Silvio Mayolo
  • 62,821
  • 6
  • 74
  • 116
  • I'm almost certain that he wants `FnOnce` and `.into_iter()`, because he already has ownership of the `Vec`: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=813976c2fc930200f3af52eefcf31d4c – Finomnis Jun 08 '22 at 15:51