早些时候我有一个Sync + Send特点SyncMessenger:
trait Messenger {
fn send_message(&self, user_id: UserId, text: &str);
}
trait SyncMessenger: Messenger + Sync + Send {}
Run Code Online (Sandbox Code Playgroud)
它的实现:
pub struct DiscordMessenger {
discord: Arc<Discord>, // (Discord is Sync and Send already)
}
impl Messenger for DiscordMessenger {
fn send_message(&self, user_id: UserId, text: &str) {
self.discord.send_message(user_id, text, false);
}
}
impl SyncMessenger for DiscordMessenger {}
Run Code Online (Sandbox Code Playgroud)
并使用它:
struct Bot {
messenger: Arc<SyncMessenger>,
}
impl Bot {
pub fn new() -> Bot {
Bot { messenger: Arc::new(DiscordMessenger::new()) }
}
fn messenger(&self) -> Arc<SyncMessenger> {
self.messenger.clone()
}
}
struct PingCommand {
fn fire(&mut self, bot: &mut Bot) {
bot.messenger().send_message(UserId(0), "Pong");
}
}
Run Code Online (Sandbox Code Playgroud)
一切正常。现在我想实现TestMessenger它并没有真正通过网络发送消息而是切换一个标志Self:
#[cfg(test)]
struct TestMessenger {
pub message_sent: bool,
}
impl Messenger for TestMessenger {
fn send_message(&mut self, user_id: UserId, text: &str) { // we have `&mut self` here
self.message_sent = true;
}
}
Run Code Online (Sandbox Code Playgroud)
所以我需要改变send_message(&self)到send_message(&mut self)任何地方(在特征和实现中)。我这样做了,但在我无法编译我的用户代码之后:
struct PingCommand {
fn fire(&mut self, bot: &mut Bot) {
bot.messenger().send_message(UserId(0), "Pong");
}
}
Run Code Online (Sandbox Code Playgroud)
给出错误:
|
12 | let _ = bot.messenger().send_message(UserId(0),
| ^^^^^^^^^^^^^^^ cannot borrow as mutable
error: aborting due to previous error
Run Code Online (Sandbox Code Playgroud)
我发现了一些有用的东西,但对我来说它看起来很丑陋(并且需要unwrap()我想避免的东西):
let _ = Arc::get_mut(&mut bot.messenger()).unwrap().send_message(UserId(0),
Run Code Online (Sandbox Code Playgroud)
所以这里的问题是如何尽可能简单地做到这一点,没有unwrap()s,像Arc::get_mut? 为什么fn messenger(&self) -> Arc<SyncMessenger>不能调用简单的mut方法?
您可以使用内部可变性来更改不可变引用背后的数据。
use std::cell::Cell;
struct TestMessenger {
pub message_sent: Cell<bool>,
}
impl Messenger for TestMessenger {
fn send_message(&self, user_id: UserId, text: &str) {
self.message_sent.set(true);
}
}
Run Code Online (Sandbox Code Playgroud)
此结构适用于单胎情况。您将需要std::sync::Mutex而不是Cell拥有SyncTestMessenger。
请注意,应严格检查所实现的特征方法的合规性:由于前者对 的可变引用,send_message(&mut self, user_id: UserId, text: &str)因此不合规,编译器最终会抱怨。send_message(&self, user_id: UserId, text: &str)self
因此,这里需要内部可变性,以便状态更改可能发生在不可变引用后面。在这种情况下,由于您正在处理其他线程安全组件,因此您可以考虑使用线程安全AtomicBool.
use std::sync::atomic::AtomicBool;
#[cfg(test)]
struct TestMessenger {
pub message_sent: AtomicBool,
}
impl Messenger for TestMessenger {
fn send_message(&self, user_id: UserId, text: &str) { // we have `&mut self` here
self.message_sent.store(true, Ordering::AcqRel);
}
}
Run Code Online (Sandbox Code Playgroud)