2017-05-23 4 views
1

Früher hatte ich ein Sync + Send Merkmal SyncMessenger:Mutable Arc in Rust

trait Messenger { 
    fn send_message(&self, user_id: UserId, text: &str); 
} 

trait SyncMessenger: Messenger + Sync + Send {} 

Es ist die Implementierung:

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 {} 

und deren Verwendung:

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"); 
    } 
} 

Alles hat gut funktioniert. Jetzt möchte ich TestMessenger implementieren, die nicht wirklich eine Nachricht über ein Netzwerk senden, sondern schaltet ein Flag in Self statt:

#[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; 
    } 
} 

Also muss ich send_message(&self)-send_message(&mut self) überall ändern (in Zügen und in Implementierungen). Ich habe das aber nachdem ich nicht meinen Benutzercode kompilieren können:

struct PingCommand { 
    fn fire(&mut self, bot: &mut Bot) { 
     bot.messenger().send_message(UserId(0), "Pong"); 
    } 
} 

gibt Fehler:

| 
12 |   let _ = bot.messenger().send_message(UserId(0), 
    |     ^^^^^^^^^^^^^^^ cannot borrow as mutable 

error: aborting due to previous error 

ich etwas gefunden haben, das funktioniert, aber es sieht sehr hässlich zu mir (und erfordert unwrap() die ich möchte vermeiden):

let _ = Arc::get_mut(&mut bot.messenger()).unwrap().send_message(UserId(0), 

die Frage ist hier, wie dass so viel einfach wie möglich zu machen, ohne unwrap() s, statische Methoden wie Arc::get_mut? Warum einfach fn messenger(&self) -> Arc<SyncMessenger> ist nicht möglich, mut Methoden aufzurufen?

Antwort

3

Beachten Sie, dass die implementierten Trait-Methoden streng auf Compliance überprüft werden sollten: send_message(&mut self, user_id: UserId, text: &str) ist nicht kompatibel mit aufgrund der veränderlichen Referenz der ehemaligen self, und der Compiler würde schließlich beschweren.

Daher ist hier eine innere Veränderlichkeit erforderlich, damit Zustandsänderungen hinter einer unveränderlichen Referenz auftreten können. Da Sie in diesem Fall mit anderen threadsicheren Komponenten arbeiten, sollten Sie die threadsichere Version AtomicBool verwenden.

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); 
    } 
} 
2

Sie können die interne Veränderbarkeit verwenden, um Daten hinter unveränderlichen Referenzen zu ändern.

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); 
    } 
} 

Diese Struktur ist für Single-Prozess-Gehäuse. Sie benötigen std::sync::Mutex anstelle von Cell, um Sync für TestMessenger zu haben.