Actix actors, "move ||" to closure seems not to move, but wants a copy instead

Hello everybody!

I have a problem with wrapping my head around move ||. I suppose it is not actix specific in any way.

Currently I have a few Actors I want to message each other:

use actix::prelude::*;

struct Main {}

impl Actor for Main {
    type Context = Context<Self>;
}

struct GUI {
    main: Addr<Main>,
}

impl Actor for GUI {
    type Context = SyncContext<Self>;
}

struct Logging {
    main: Addr<Main>,
}

impl Actor for Logging {
    type Context = SyncContext<Self>;
}

I managed to wire it correctly, but have no idea why must it be like that.

Attempt 1

I know why this doesn't compile. Closure of the 1st arbiter steals maddr then clones it, therefore there's noting to move to Logging. I tried all variants with references, wit/without move, all errors are clear what's wrong.

#[actix_rt::main]
async fn main() {
    let maddr = Main {}.start();
    let gaddr = SyncArbiter::start(1, move || {
        GUI {
            main: maddr.clone(),
        }
    });
    let laddr = SyncArbiter::start(1, move || {
        Logging {
            main: maddr.clone(),
        }
    });

    // Irrelevant code to wait for ^C
}

Attempt 2

I have no idea, why this doesn't work... Both m1 and m2 IMHO can be cleanly moved into closures.

#[actix_rt::main]
async fn main() {
    let maddr = Main {}.start();
    let m1 = maddr.clone();
    let gaddr = SyncArbiter::start(1, move || GUI { main: m1 });

    let m2 = maddr.clone();
    let laddr = SyncArbiter::start(1, move || Logging { main: m2 });

    // Irrelevant code to wait for ^C
}

Error:

error[E0507]: cannot move out of `m1`, a captured variable in an `Fn` closure
  --> cw4t_dmk_testers/src/bin/act.rs:29:59
   |
28 |     let m1 = maddr.clone();
   |         -- captured outer variable
29 |     let gaddr = SyncArbiter::start(1, move || GUI { main: m1 });
   |                                       --------------------^^--
   |                                       |                   |
   |                                       |                   move occurs because `m1` has type `actix::Addr<Main>`, which does not implement the `Copy` trait
   |                                       captured by this `Fn` closure

Attempt 3

This works

#[actix_rt::main]
async fn main() {
    let maddr = Main {}.start();
    let m1 = maddr.clone();
    let gaddr = SyncArbiter::start(1, move || GUI { main: m1.clone() });

    let m2 = maddr.clone();
    let laddr = SyncArbiter::start(1, move || Logging { main: m2.clone() });

    // Irrelevant code to wait for ^C
}

I'm fine with double-cloning (this is done once per appplication run), but why?

As the error message says, the callable is expected to be an Fn, i.e., it is required to be callable multiple times. That of course doesn't work if it consumes the capture, it must reference it.

1 Like

Thank you. Of course now it makes absolute sense.