具有可为空的Option字段的Rust结构

Har*_*olt 6 optional rust

在python中,我可能有一个看起来像这样的类:

class ClientObject(object):

    def __init__(self):
        connection = None
        connected = False

    def connect(self):
        self.connection = new Connection('server')
        self.connected = True

    def disconnect(self):
        self.connection.close()
        self.connection = None
        self.connected = False
Run Code Online (Sandbox Code Playgroud)

我正在尝试做类似生锈的事情.首先,我怀疑这是否是一个生锈的好模式 - 你会用这种方式实现一个连接的客户端类吗?其次,我收到一个错误,我不理解我的实现.

pub struct Client {
    seq: int,
    connected: bool,
    socket: Option<UdpSocket>
}

impl Client {

    pub fn connect(&mut self, addr: &SocketAddr) -> ClientConnectionResult {
        match self.socket {
            Some(_) => self.disconnect(),
            None => ()
        };

        self.socket = match UdpSocket::bind(*addr) {
            Ok(s) => Some(s),
            Err(e) => return Err(to_client_error(e))
        };

        self.connected = true;

        Ok(())
    }

    pub fn disconnect(&mut self) {
        match self.socket {
            None => (),
            Some(s) => drop(s)
        };

        self.socket = None;

        self.connected = false;
    }
}
Run Code Online (Sandbox Code Playgroud)

在disconnect函数中,匹配会生成编译错误,因为它会尝试移动self.socket的所有权.我想要做的是将self.socket设置为None,并允许稍后在调用connect时将其重新分配给某些内容.我该怎么办?

Vla*_*eev 9

它可能不适用于您的用例,但Rust移动语义和强类型允许廉价的"状态机",当方法消耗self(当前状态)并返回另一个表示另一个状态的对象时.这是如何TcpListener实现的:它具有listen()返回的方法,TcpAcceptor在进程中消耗原始侦听器.这种方法具有键入的优点:当对象处于无效状态时,您无法调用无意义的方法.

在你的情况下,它可能看起来像这样:

use std::kinds::markers::NoCopy;

pub struct DisconnectedClient {
    seq: int,
    _no_copy: NoCopy
}

impl DisconnectedClient {
    #[inline]
    pub fn new(seq: int) -> DisconnectedClient {
        DisconnectedClient { seq: seq, _no_copy: NoCopy }
    }

    // DisconnectedClient does not implement Copy due to NoCopy marker so you need 
    // to return it back in case of error, together with that error, otherwise 
    // it is consumed and can't be used again.
    pub fn connect(self, addr: &SocketAddr) -> Result<ConnectedClient, (DisconnectedClient, IoError)> {
        match UdpSocket::bind(*addr) {
            Ok(s) => Ok(ConnectedClient { seq: self.seq, socket: s }),
            Err(e) => Err((self, e))
        }
    }
}

pub struct ConnectedClient {
    seq: int,
    socket: UdpSocket
}

impl ConnectedClient {
    #[inline]
    pub fn disconnect(self) -> DisconnectedClient {
        // self.socket will be dropped here
        DisconnectedClient::new(self.seq)
    }

    // all operations on active connection are defined here
}
Run Code Online (Sandbox Code Playgroud)

您首先创建DisconnectedClient使用DisconnectedClient::new()方法.然后,当您想要连接到某个东西时,您使用connect()方法,该方法使用DisconnectedClient并返回新对象ConnectedClient,它表示已建立的连接.完成此连接后,disconnect()方法将ConnectedClient返回DisconnectedClient.

这种方法可能更复杂,但它具有静态检查错误状态的优点.你没有必要connected()的方法; 如果您的变量是类型ConnectedClient,那么您已经知道它已连接.