Почему Arc :: try_unwrap () вызывает панику? - PullRequest
0 голосов
/ 09 декабря 2018

Я пишу простой чат-сервер, который передает сообщения всем подключенным клиентам.

Код может выглядеть ужасно, так как я новичок.Одноранговые узлы еще нигде не используются, поскольку я также хочу передать их в функцию 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 голосов
/ 09 декабря 2018

В документации говорится

Возвращает содержащееся в нем значение, если Arc имеет ровно одну сильную ссылку.

В противном случае Err возвращается с тем же Arc, который был передан.

Это будет успешным, даже если имеются невыполненные слабые ссылки.

(слабая ссылка )

Ваш код будет хорошо работать с одной сильной и множеством слабых ссылок.

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

Следует отметить одну вещь о слабых ссылках: если вы развернете свою единственную сильную ссылку, вы не сможете использовать слабые ссылки.

0 голосов
/ 09 декабря 2018

Почему это паникует?

Вы звоните unwrap на Result::Err.Err происходит из-за сбоя try_unwrap на Arc.

Что я делаю не так?

Развертывание Arc приведет к движению его стоимость и завладеть ею.Это терпит неудачу, потому что есть три клона Arc:

  • один в основном цикле, который все еще находится в области действия
  • один в peers векторе
  • тот, который вы пытаетесь развернуть внутри handle_client.

Два других клона станут недействительными, если Rust позволит вам развернуть и переместить значение.Вместо того, чтобы развернуть значение, вы можете использовать реализацию Arc Deref, чтобы заимствовать его:

let stream: &TcpStream = &arc;

Поскольку вы сейчас заимствуете значение из Arc, вам необходимо переместить область действияпеременная arc внутри нового потока, в противном случае средство проверки заимствования не сможет гарантировать, что оно будет существовать так же долго, как поток:

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

        // ... 
     }
}
...