在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
我正在尝试做类似生锈的事情。首先,我怀疑这是否是一个生锈的好模式 - 你会用这种方式实现一个连接的客户端类吗?其次,我收到的错误是我对我的实施不了解。
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;
}
}
在disconnect函数中,匹配会生成编译错误,因为它会尝试移动self.socket的所有权。我想要做的是将self.socket设置为None,并允许稍后在调用connect时将其重新分配给某些内容。我该怎么做?
答案 0 :(得分:9)
它可能不适用于您的用例,但是当方法使用self
(当前状态)并返回表示另一个状态的另一个对象时,Rust移动语义和强类型允许便宜的“状态机”。这就是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
}
首先使用DisconnectedClient
方法创建DisconnectedClient::new()
。然后,当您想要连接某些内容时,使用connect()
方法,该方法使用DisconnectedClient
并返回表示已建立连接的新对象ConnectedClient
。使用此连接后,disconnect()
方法会将ConnectedClient
转回DisconnectedClient
。
这种方法可能更复杂,但它具有静态检查错误状态的优点。您不必拥有connected()
- 类似的方法;如果您的变量属于ConnectedClient
类型,那么您已经知道它已连接。
答案 1 :(得分:2)
如果你的Python代码self.connected
等同于self.connection is not None
,那么布尔值是完全浪费时间和空间;你最好省略它,或者它是否有价值作为财产:
@property
def connected(self):
return self.connection is not None
同样在Rust中你可以单独存储布尔值,但绝对没有意义。
impl Client {
pub fn connect(&mut self, addr: &SocketAddr) -> ClientConnectionResult {
self.disconnect();
self.socket = Some(try!(UdpSocket::bind(*addr)));
Ok(())
}
pub fn disconnect(&mut self) {
self.socket = None;
}
}
就移动的self.socket
部分而言,你不能像你在那里那样做,因为正在移动它。你必须把东西放在原处。这可以通过类似.take()
。
drop
是不必要的;当值超出范围或以其他方式替换时,将调用析构函数,self.socket = None
将导致这种情况发生。同样,因为如果disconnect()
已经socket
,None
工作正常,则if self.socket.is_some()
的调用不需要self.disconnect()
检查(或等效模式匹配)在connect
。
如果您希望某人能够检查它是否已连接,请为其制作一个简单的方法:
pub fn connected(&self) -> bool {
self.socket.is_some()
}