3

I'm writing a simple chat server which broadcasts messages to all the clients connected.

The code might look terrible, since I'm a beginner. Peers are not used anywhere yet, since I want to pass it to handle_client function as well, so when data will be available in stream and read successfully, I want to broadcast it over all the clients connected. I understand this is not a good approach, I'm just trying to understand how can I do things like this in general.

use std::io::BufRead;
use std::io::Write;
use std::net::{TcpListener, TcpStream};
use std::sync::Arc;

fn handle_client(arc: Arc<TcpStream>) -> std::io::Result<()> {
    let mut stream = Arc::try_unwrap(arc).unwrap();

    stream.write(b"Welcome to the server!\r\n")?;
    println!("incomming connection: {:?}", stream);

    std::thread::spawn(move || -> std::io::Result<()> {
        let peer_addr = stream.peer_addr()?;
        let mut reader = std::io::BufReader::new(stream);
        let mut buf = String::new();

        loop {
            let bytes_read = reader.read_line(&mut buf)?;
            if bytes_read == 0 {
                println!("client disconnected {}", peer_addr);
                return Ok(());
            }

            buf.remove(bytes_read - 1);
            println!("{}: {}", peer_addr, buf);

            buf.clear();
        }
    });

    Ok(())
}

fn start() -> std::io::Result<()> {
    let listener = TcpListener::bind("0.0.0.0:1111")?;
    println!("listening on {}", listener.local_addr()?.port());

    let mut peers: Vec<Arc<TcpStream>> = vec![];

    for stream in listener.incoming() {
        let mut stream = stream.unwrap();
        let arc = Arc::new(stream);

        peers.push(arc.clone());
        handle_client(arc.clone()).unwrap();
    }

    Ok(())
}

fn main() -> std::io::Result<()> {
    start()
}

It compiles fine, but let mut stream = Arc::try_unwrap(arc).unwrap(); in the handle_client function panics. What am I doing wrong? Why is it panicking?

trent
  • 25,033
  • 7
  • 51
  • 90
Nika
  • 1,864
  • 3
  • 23
  • 44

2 Answers2

3

Why is it panicking?

You are calling unwrap on a Result::Err. The Err comes from try_unwrap failing on the Arc.

What am I doing wrong?

Unwrapping an Arc will move its value and take ownership of it. This fails because there are three clones of the same Arc:

  • one in the main loop which is still in scope
  • one in the peers vector
  • the one that you are trying to unwrap inside handle_client.

The other two clones would become invalid if Rust allowed you to unwrap and move the value. Instead of unwrapping the value you can use Arc's Deref implementation to borrow it:

let stream: &TcpStream = &arc;

Since you are now borrowing the value from the Arc, you need to move the scope of the arc variable inside the new thread, otherwise the borrow checker won't be able to ensure that it lives as long as the thread:

fn handle_client(arc: Arc<TcpStream>) -> std::io::Result<()> {
    std::thread::spawn(move || -> std::io::Result<()> {
        let mut stream: &TcpStream = &arc;
        stream.write(b"Welcome to the server!\r\n")?;

        let peer_addr = stream.peer_addr()?;
        let mut reader = std::io::BufReader::new(stream);
        let mut buf = String::new();

        // ... 
     }
}
loganfsmyth
  • 156,129
  • 30
  • 331
  • 251
Peter Hall
  • 53,120
  • 14
  • 139
  • 204
  • Well, if I comment the `peers.push(arc.clone())`, it still panics. What I found out is that, after removing `.clone` it works fine. – Nika Dec 09 '18 at 11:28
  • 1
    There are **two** other references. The other is the `arc` variable inside the loop, whose scope is until the end of the loop. When you remove the `clone`, you instead move the variable, so it is no longer alive in the loop. – Peter Hall Dec 09 '18 at 11:31
1

It says in the documentation

Returns the contained value, if the Arc has exactly one strong reference.

Otherwise, an Err is returned with the same Arc that was passed in.

This will succeed even if there are outstanding weak references.

(weak reference)

Your code will work fine with one strong and many weak references.

let mut peers: Vec<Weak<TcpStream>> = vec![];

for stream in listener.incoming() {
    let mut stream = stream.unwrap();
    let arc = Arc::new(stream);
    peers.push(Arc::downgrade(&arc));
    handle_client(arc).unwrap();
}

One thing to note about the weak references: if you unwrap your one strong reference, you will not able to use weak references.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Ömer Erden
  • 7,680
  • 5
  • 36
  • 45