循环Rust集合(不可变地借用),然后对其进行变异

hc8w905p  于 2023-05-18  发布在  其他
关注(0)|答案(1)|浏览(157)

我在尝试在Rust中操作HashMap时遇到了麻烦。

use std::os::fd::AsRawFd;
use std::collections::HashMap;
use std::collections::HashSet;
use tokio::net::TcpListener;
use tokio::sync::mpsc;

#[tokio::main]
async fn main() -> std::io::Result<()>{
    let (tx, mut rx) = mpsc::channel(32);
    let listener = TcpListener::bind("127.0.0.1:1234").await?;
    
    tokio::spawn(async move {
        while let Ok((stream, _)) = listener.accept().await {
            tx.send(stream).await;
        }
    });

    let mut should_close = HashSet::new();
    let mut clients = HashMap::new();
    let mut buf = [0u8; 1024];
    loop {
        if let Ok(stream) = rx.try_recv() {
            // clients.entry(stream.as_raw_fd()).or_insert(stream);
            clients.insert(stream.as_raw_fd(), stream);
        }
        should_close.clear();
        
        for (fd, stream) in clients.iter() {
            match stream.try_read(&mut buf) {
                Ok(0) => {
                    println!("Client {fd} disconnected");
                    should_close.insert(fd);
                },
                Ok(_) => {
                    let s = String::from_utf8(buf.to_vec()).unwrap();
                    println!("Client {fd} says: {}", s);
                    stream.try_write(format!("Thank you for saying: {s}").as_bytes());
                },
                Err(e) if e.kind() == tokio::io::ErrorKind::WouldBlock => continue,
                Err(e) => {
                    println!("Client {fd} error: {e}");
                    should_close.insert(fd);
                }
            }
        }
        for fd in &should_close {
            // clients.remove_entry(fd);
            clients.remove(fd);
        }
        
    }

Playground
由于这是一个TCP服务器,我不想为每个连接的客户端生成一个任务并为其分配一个专用缓冲区,因此我将客户端列表存储在变量clients中。在无限循环中,如果rx从侦听器接收到一个新的客户端,则该客户端被添加到clients。然后我在这个Map上循环,以非阻塞方式读取客户端的消息,在should_close变量中跟踪应该取消跟踪的客户端。完成对clients的循环后,我对should_close进行循环以删除clients中的元素。
编译器不会让我借用可变和不可变的可互换。以下是产生的错误:

error[E0502]: cannot borrow `clients` as mutable because it is also borrowed as immutable
  --> src/main.rs:21:13
   |
21 |             clients.insert(stream.as_raw_fd(), stream);
   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
22 |         }
23 |         should_close.clear();
   |         -------------------- immutable borrow later used here
24 |         
25 |         for (fd, stream) in clients.iter() {
   |                             -------------- immutable borrow occurs here

将Map Package 在RefCell甚至Rc<RefCell>中不起作用:

error[E0716]: temporary value dropped while borrowed
  --> src/main.rs:25:29
   |
25 |         for (fd, stream) in clients.borrow().iter() {
   |                             ^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use
...
42 |         }
   |         - temporary value is freed at the end of this statement
43 |         for fd in &should_close {
   |                   ------------- borrow later used here
   |
   = note: consider using a `let` binding to create a longer lived value

编译器建议在循环外创建一个let *,但这不起作用,因为在循环内借用mutable来改变map会导致异常。
正确的方法是什么?

qyzbxkaa

qyzbxkaa1#

你的问题发生是因为你的哈希集should_close存储 * 引用 * 到clients的键。尝试按值存储描述符:

let mut clients: HashSet<RawFd> = HashMap::new();
...
for (&fd, stream) in clients.iter() {
   ...
}

应该可以
在这种情况下,在声明变量时将变量类型写完整通常是很有见地的。这些推断的变量类型可能会造成混淆。

相关问题