为什么Arc :: try_unwrap()引起恐慌?

时间:2018-12-09 11:07:11

标签: error-handling rust automatic-ref-counting smart-pointers

我正在编写一个简单的聊天服务器,该服务器将消息广播到所有连接的客户端。

由于我是初学者,因此代码看起来可能很糟糕。由于我也希望将对等传递给handle_client函数,因此尚未在任何地方使用对等体,因此,当数据在流中可用并成功读取时,我想在所有连接的客户端上广播。我知道这不是一个好方法,我只是想了解我一般如何做这样的事情。

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()
}

它可以正常编译,但是let mut stream = Arc::try_unwrap(arc).unwrap();函数中的handle_client出现了问题。我究竟做错了什么?为什么会慌张?

2 个答案:

答案 0 :(得分:3)

  

为什么会惊慌?

您正在unwrap上呼叫Result::ErrErr来自try_unwrap,但在Arc上失败。

  

我在做什么错了?

解开Arc移动并获取其所有权。之所以失败,是因为存在三个相同Arc的克隆:

  • 主循环中仍在作用域中的一个
  • peers向量中的一个
  • 您要在handle_client内部展开的那个。

如果Rust允许您解开并移动值,则其他两个克隆将变得无效。 不用解开值,您可以使用Arc的{​​{1}}实现来借用它:

Deref

由于您现在是从let stream: &TcpStream = &arc; 借用值,因此需要在新线程内移动Arc变量的范围,否则借用检查器将无法确保它只要线程保持生命:

arc

答案 1 :(得分:1)

它在the documentation

中说
  

如果Arc的正整数为1,则返回包含的值   参考。

     

否则,将返回一个Err并传递相同的Arc

     

即使存在突出的弱引用,此操作也会成功。

weak reference

您的代码可以很好地使用一个强引用和许多弱引用。

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();
}

关于弱引用的一件事要注意:如果拆开一个强引用,则将无法使用弱引用。