如何移动借用的内容以关闭连接

How to move borrowed content to close connection

以下代码应该接受 tcp 连接,从中读取并在挂断时关闭它们。

extern crate mio;
use mio::{EventLoop,Token,ReadHint};
use mio::tcp::{TcpListener, TcpStream};
use std::io::Read;

const L_CLIENT: Token = Token(0);
const C_CLIENT: Token = Token(1);

fn sa(port:u16) -> std::net::SocketAddr {
  let ipa : std::net::Ipv4Addr = std::net::Ipv4Addr::new(127,0,0,1);
  std::net::SocketAddr::V4(std::net::SocketAddrV4::new(ipa,port))
}

fn main(){
  let mut event_loop = EventLoop::new().unwrap();

  let lclient = TcpListener::bind(&sa(4000)).unwrap();
  event_loop.register(&lclient, L_CLIENT).unwrap();

  println!("running loop...");
  event_loop.run(&mut MyHandler{lclient:lclient,cclient:None}).unwrap();
  println!("done.");
}

struct MyHandler{
  lclient:TcpListener,
  cclient:Option<TcpStream>,
}

impl mio::Handler for MyHandler {
  type Timeout = ();
  type Message = ();

  fn readable(&mut self, event_loop: &mut EventLoop<Self>, token: Token, hint: ReadHint){
    match token {
      L_CLIENT => {
        let s=self.lclient.accept().unwrap().expect("no client??");
        match self.cclient{
          None => {event_loop.register(&s,C_CLIENT);self.cclient=Some(s);}
          Some(_) =>{println!("ignore second client");} // s should be closed as it goes out of scope
        }
      },
      C_CLIENT => {
        let mut client=self.cclient.expect("connection is gone"); // what's the problem here?
        if hint.is_hup() {
          event_loop.deregister(&client);
          self.cclient=None; // how to close connection? 
        } else  {
          let mut buf: [u8; 500] = [0; 500];
          let l=client.read(&mut buf).unwrap();
          println!("read {} bytes.",l);
        }
      },
      _ =>{println!("other Token");}
    }
  }
}

这几乎行得通。在 C_CLIENT 案例中,我想处理客户端挂断。在那种情况下,我想我需要将连接的所有权从我的处理程序转移到该方法,以便让它超出范围以关闭它。

不幸的是生锈不允许我移动连接:

 error: cannot move out of borrowed content

据我了解,问题是 &self 是在 readable 方法中借用的,因此无法访问其字段。

如何访问连接? (避免上述错误。)

如何关闭该连接? (即转让所有权。)

我假设原始代码中的错误来自这一行:

let mut client=self.cclient.expect("connection is gone");

这是不允许的,因为 Option::expect 拥有其参数的所有权,但 readable 仅具有借用的引用。只有所有者才能将值移动给新所有者。

您可以使用 Option:::take 解决此问题。这不是移动原始选项,而是借用它并对其进行变异,将其更改为 None 并返回其原始值:

let client = self.cclient.take().expect("connection is gone");
// Now `client` is a TcpStream and `self.cclient` is None.

但是,根本没有必要移动值。你已经有了一个可变引用,所以你可以就地改变它:

C_CLIENT => {
    if hint.is_hup() {
        event_loop.deregister(&self.cclient);
        self.cclient = None;
    } else  {
        let mut buf: [u8; 500] = [0; 500];
        let l = self.cclient.read(&mut buf).unwrap();
        println!("read {} bytes.",l);
    }
},

当您写入 self.cclient = None; 时,self.cclient 的旧值会立即被删除,从而关闭连接。在 Rust 中,当一个值不再被拥有时,它就会被丢弃。当其所有者超出范围或重新分配其所有者时,可能会发生这种情况。当变量超出范围或被重新分配时,Rust 编译器静态插入 drop 调用。