我对如何编写并发异步代码封装在一个单一结构中感到困惑。
我不确定如何准确地解释这个问题,所以我将尝试通过一个例子来说明。
假设我有一个名为 `UdpServer` 的结构体。该结构体有多个与其行为相关的方法(例如 `handle_datagram`、`deserialize_datagram` 等)。 如果要使代码并发,我将生成一个 tokio 任务,这需要提供给它的闭包是静态的,这意味着我不能从此任务中调用 `&self`,只要 `&self` 不是静态的,这意味着我无法调用 `self.serialize_datagram()`。
我理解这个问题(没有保证结构体会比线程更长时间存在),但是找不到适当的解决方法。我知道可以将函数移出 impl,但这对我来说不是一个好的解决方法。 此外,即使我们暂时假设我可以将`&self`作为静态参数,对我来说,这段代码看起来仍然不正确(可能是因为不够 Rusty)。 另一种“解决方案”是使用 `self: Arc` 代替 `&self`,但这甚至更糟糕。
因此,我假设我不知道的某种模式。 谁能向我解释应该如何重构整个代码?
示例代码:
我不确定如何准确地解释这个问题,所以我将尝试通过一个例子来说明。
假设我有一个名为 `UdpServer` 的结构体。该结构体有多个与其行为相关的方法(例如 `handle_datagram`、`deserialize_datagram` 等)。 如果要使代码并发,我将生成一个 tokio 任务,这需要提供给它的闭包是静态的,这意味着我不能从此任务中调用 `&self`,只要 `&self` 不是静态的,这意味着我无法调用 `self.serialize_datagram()`。
我理解这个问题(没有保证结构体会比线程更长时间存在),但是找不到适当的解决方法。我知道可以将函数移出 impl,但这对我来说不是一个好的解决方法。 此外,即使我们暂时假设我可以将`&self`作为静态参数,对我来说,这段代码看起来仍然不正确(可能是因为不够 Rusty)。 另一种“解决方案”是使用 `self: Arc` 代替 `&self`,但这甚至更糟糕。
因此,我假设我不知道的某种模式。 谁能向我解释应该如何重构整个代码?
示例代码:
struct UdpServer {}
impl UdpServer {
pub async fn run(&self) {
let socket = UdpSocket::bind(self.addr).await.unwrap();
loop {
let mut buf: &mut [u8] = &mut [];
let (_, _) = socket.recv_from(&mut buf).await.unwrap();
// I spawn tokio task to enable concurrency
tokio::spawn(async move {
// But i can't use &self in here because it's not static.
let datagram = self.deserialize_datagram(buf).await;
self.handle_datagram(()).await;
});
}
}
pub async fn deserialize_datagram(&self, buf: &mut [u8]) -> Datagram {
unimplemented!()
}
pub async fn handle_datagram(&self, datagram: Datagram) {
unimplemented!()
}
}
self: Arc<Self>
而不是&self,但这种方式甚至更糟糕。为什么你觉得这样更糟糕呢?在我看来,这似乎是一个完全有效的解决方案。 - Aplet123self.clone()
。 - Bonanovclosure
crate 旨在使克隆发送到闭包的变量更加容易。 - user4815162342