Help with fixing 'Use of moved value'

Hi!
I’m trying to build a TCP server which would broadcast the same message to multiple clients.
This is how I would approach it normally:

  1. Have a ‘Connection’ type which would hold the connection information
  2. Have a list of connections where I would push new connections
  3. Iterate over connection list and send messages to every connection

Here is my attempt of doing it:

Connection struct and a list of connections:

struct Connection {
    sender: channel::Sender<String>,
    id: usize
}

fn main() -> io::Result<()> {

let mut connections: Vec<Connection> = Vec::new();

Sending messages in a loop to all of the clients:

thread::spawn(move|| {
    loop {
        for i in 1..1000 {

            for connection in connections.iter() {
                println!("DEBUG messages in the channel {:?}", connection.sender.len());
                connection.sender.send(format!("Test message {}", i));
            }

            thread::sleep(time::Duration::from_millis(500));
        }
    }
});

Attempt to add a new connection to the list:

let listener = TcpListener::bind("0.0.0.0:9999")?;

let mut client_count = 0;

for stream in listener.incoming() {
    client_count += 0;

    let (s, r) = channel::unbounded();
    let connection = Connection {
        sender: s,
        id: client_count
    };

    connections.push(connection); // use of moved value `connections`

    thread::spawn(|| {
        handle_client(stream.unwrap(), r);
    });
}

I vaguely understand that connections is in different scope because it’s been used first in for connection in connections.iter() { which is inside of thread::spawn. Is my understanding correct?

Anyway, I’m completely lost in how could I fix it or use a different approach to achieve a similar result?
Could someone provide me guidance on how would I do that?

Cheers,
Leonti

Solved the issue by using Arc and RWLock using an example I found on the internet:

    let connections_arc: Arc<RwLock<Vec<Connection>>> = Arc::new(RwLock::new(Vec::new()));

    let connections = Arc::clone(&connections_arc);
    thread::spawn(move|| {
        loop {
            for i in 1..1000 {

                for connection in connections.read().unwrap().iter() {
                    println!("DEBUG messages in the channel {:?}", connection.sender.len());
                    connection.sender.send(format!("Test message {}", i));
                }

                thread::sleep(time::Duration::from_millis(500));
            }
        }
    });

and when new client is connected:

    let arc_w = connections_arc.clone();

    for stream in listener.incoming() {
        client_count += 0;

        let (s, r) = channel::unbounded();
        let connection = Connection {
            sender: s,
            id: client_count
        };

        {
            let mut arc_w = arc_w.write().unwrap();
            arc_w.push(connection);
        } // write lock is released at the end of this scope

I don’t fully understand what’s going on there, but it works and it’s alright for now :slight_smile:
Will keep learning.

Yup, that’s the good solution.

What’s happening is that your original code created a Vec<Connection>, which was then moved into the closure (via move ||), so it would no longer be available for use elsewhere. By wrapping it in Arc and cloning it, you’ve made it so that you are moving a copy of the Arc (which is more like a handle or pointer to the Vec), rather than the whole Vec. This means you can access the Vec<Connection> from multiple threads, so long as the each have their own copy of the Arc.

Additionally, you needed to use an RwLock or Mutex to permit modification of the Vec, because Arc doesn’t normally allow that. (It is not safe to mutate a value from multiple threads unless they synchronize their accesses to the data, which these allow you to do by locking it.)