2

I'm building a chained future

ActorFuture<Item = Vec<String>, Error = Vec<String>, Actor = Self>

On success it will have vector of string outputs of all futures chained with .and_then. On first error processing will stop, and I want to return succeeded futures outputs and final (failed) future error. I want to use same vector to handle both paths: ok and err. But the compiler complains:

242 |                             .map(|o| {v.push(o); v})
    |                                  --- value moved (into closure) here
243 |                             .map_err(|e| {v.push(format!("{}", e)); v})
    |                                           ^ value captured here after move

Why is that? Is it possible to go both in map and map_err at once? This should never happen in my understanding.

An example:

#[test]
fn test_map_and_map_err() {
    let mut v = Vec::new();
    Ok("foo".to_string())
        .map(|i| { v.push(i); v })
        .map_err(|e: String| { v.push(e); v });
}
error[E0382]: capture of moved value: `v`
 --> src/lib.rs:6:32
  |
5 |         .map(|i| { v.push(i); v })
  |              --- value moved (into closure) here
6 |         .map_err(|e: String| { v.push(e); v });
  |                                ^ value captured here after move
  |
  = note: move occurs because `v` has type `std::vec::Vec<std::string::String>`, which does not implement the `Copy` trait
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
tworec
  • 4,409
  • 2
  • 29
  • 34
  • _"Why is that? I mean, is it possible to go both in map and map_err at once?"_ — I'm sure it is. But you don't provide enough information to help. – Peter Hall Sep 20 '18 at 10:15

1 Answers1

6

The compiler doesn't know that the closures passed to map and map_err are mutually exclusive. Even if it did, it has no way of constructing two closures that both own the same value.

You need to use a construct that is "transparent" to the compiler, like match:

#[test]
fn test_map_and_map_err() {
    let mut v = Vec::new();
    let r = Ok("foo".to_string())
        .map(|i| v.push(i))
        .map_err(|e: String| v.push(e));
    match r {
        Ok(()) => Ok(v),
        Err(()) => Err(v),
    };
}

Or you can swap the value out instead:

#[test]
fn test_map_and_map_err() {
    use std::mem;
    let mut v = Vec::new();
    Ok("foo".to_string())
        .map(|i| {
            v.push(i);
            mem::replace(&mut v, vec![])
        }).map_err(|e: String| {
            v.push(e);
            mem::replace(&mut v, vec![])
        });
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
DK.
  • 55,277
  • 5
  • 189
  • 162
  • 1
    In the `match`, I would just move the logic inside: `match Ok("foo".to_string()) { Ok(s) => { v.push(s); Ok(v) } Err(e) => { v.push(e); Err(v) }` – trent Sep 20 '18 at 14:52