如果不实现 Copy 或 Clone 特征,重用借用的结构字段的最佳方法是什么?

What is the best way to reuse a borrowed struct field if it doesn't implement the Copy or Clone trait?

我正在为 TCP 服务器编写客户端

use std::net::TcpStream;
use std::io::{Read, Write, Error};

pub struct Client {
    addr: String,
    conn: Option<TcpStream>,
}

impl Client {
    pub fn connect(&mut self) {
        let res = TcpStream::connect(&self.addr);
        match res {
            Ok(c) => {
                self.conn = Some(c);
            },
            Err(_) => panic!(),
        }
    }

    pub fn version(self) -> Result<[u8; 8], Error> {
        let mut ver: [u8; 8] = [0;8];
        let command_string = b"VERSION\r\n";
        let res = self.conn.unwrap().write(&command_string[0..]);
        match res {
            Ok(_) => self.conn.unwrap().read(&mut ver[..]),
            Err(e) => panic!(e)
        };
        Ok(ver)
    }
}

版本方法returns错误

error[E0382]: use of moved value: `self.conn`

当我尝试重新使用参考时。我考虑过使用 #[derive(Copy, Clone)] 但由于这些特性没有为 TCPStream 实现,我不确定该怎么做。在一个方法中实现读取和写入服务器的最佳方法是什么(顺便说一句,会有很多这样的方法,比如既可以读取也可以写入服务器的版本)

我对 Rust 还是个新手,并试图围绕所有权概念思考,所以如果这是一个愚蠢的问题,我深表歉意,但因为我想讨论各种方法(如果确实有多种方法来解决这个问题) reuse un-Clonable/Copyable fields 我认为这个问题值得一问。

The version method returns an error when I try to reuse the reference

self.conn 不是引用;这是一个拥有的价值。因为 Option::unwrap 按值获取它(并且它的类型没有实现 Copy),所以在调用 .unwrap() 之后不能重用它;那会导致不安全。

但是您可以将调用 unwrap 的结果存储到一个新变量中,然后改用它:

    pub fn version(self) -> io::Result<[u8; 8]> {
        let mut ver: [u8; 8] = [0;8];
        const COMMAND_STRING: &[u8] = b"VERSION\r\n";
        let mut conn = self.conn.unwrap();
        conn.write(COMMAND_STRING)?;
        conn.read(&mut ver[..])?;
        Ok(ver)
    }

这是可能的,因为io::Write::writeio::Read::read都取&mut self,这意味着两者都不需要按值取conn。相反,conn 将在 version 的末尾删除。另见

我做了一些进一步的修改,你可以考虑这些建议:

  • io::Result<_>Result<_, io::Error> 的别名,所以我更改了声明的 return 类型。这是许多库中的常见模式,我发现这种形式更容易快速理解。
  • conn.read() returns 一个 io::Result,之前被忽略了。现在 conn.write()conn.read() 都使用特殊的 ? 运算符向调用者引发错误。如果你想让错误恐慌而不是传播,你可以用 .unwrap() 替换 ? 。参见
  • COMMAND_STRING 是一只 const 蚂蚁。与 conn.write(b"VERSION\r\n") 相比,使用这个名称是否有用还有待商榷,但如果您要使用它,const 似乎是正确的选择。原则上编译器可以比常规变量更好地优化 consts;在实践中,在这种情况下不太可能产生太大差异,但是当 const 更明确地说明该值的使用方式时,没有理由不这样做。

使用实际参考

如果您确实希望 version 通过引用获取 self,因此您 不希望 它解包并销毁 self.conn,您可以使用 Option::as_ref 来完成这项工作。

    pub fn version(&self) -> io::Result<[u8; 8]> {  // NOTE: `&self`
        let mut ver: [u8; 8] = [0;8];
        let mut conn = self.conn.as_ref().unwrap(); // NOTE: `.as_ref()`
        conn.write(b"VERSION\r\n")?;
        conn.read(&mut ver[..])?;
        Ok(ver)
    }

Playground link

as_ref&Option<_> 变成 Option<&_>,这样 unwrapping 就不会消耗 self.conn 而只是 returns参考资料。参见