之前我有一个Sync + Send
特征SyncMessenger
:
trait Messenger {
fn send_message(&self, user_id: UserId, text: &str);
}
trait SyncMessenger: Messenger + Sync + Send {}
它的实施:
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 {}
使用它:
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");
}
}
一切都很好。现在我想实现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;
}
}
所以我需要将send_message(&self)
更改为send_message(&mut self)
无处不在(在特征和实现中)。我这样做但是在我无法编译用户代码之后:
struct PingCommand {
fn fire(&mut self, bot: &mut Bot) {
bot.messenger().send_message(UserId(0), "Pong");
}
}
给出错误:
|
12 | let _ = bot.messenger().send_message(UserId(0),
| ^^^^^^^^^^^^^^^ cannot borrow as mutable
error: aborting due to previous error
我找到了一些有效的东西,但它对我来说看起来很难看(需要unwrap()
我想避免):
let _ = Arc::get_mut(&mut bot.messenger()).unwrap().send_message(UserId(0),
所以这里的问题是如何在没有unwrap()
的静态方法Arc::get_mut
的情况下尽可能简单地做到这一点?为什么简单的fn messenger(&self) -> Arc<SyncMessenger>
无法调用mut
方法?
答案 0 :(得分:3)
请注意,应严格检查所实施的特征方法的合规性:send_message(&mut self, user_id: UserId, text: &str)
由于前者对send_message(&self, user_id: UserId, text: &str)
的可变引用而不符合self
,并且编译器最终会抱怨。< / p>
因此,此处需要内部可变性,因此状态更改可能发生在不可变引用之后。在这种情况下,由于您正在处理其他线程安全的组件,因此您可以考虑使用线程安全的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);
}
}
答案 1 :(得分:2)
您可以使用内部可变性来更改不可变引用之后的数据。
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);
}
}
此结构适用于单线程案例。您需要std::sync::Mutex
代替Cell
才能拥有Sync
TestMessenger。