I am having difficulties with designing a component when members of a struct get into fight over self
references.
use std::collections::{HashMap, VecDeque};
use std::net::TcpStream;
use std::sync::Arc;
use async_std::sync::Mutex;
struct Broker {
conn: TcpStream,
metadata_cache: HashMap<String, String>,
/// Buffer is behind mutex because it is being truncated by sending "worker process" (spawn),
/// i.e. it is mutably owned by Broker for purpose of `add' and it is mutably owned by send
/// task for purpose of read and truncate.
buffer: Arc<Mutex<Buffer>>,
}
struct Buffer (VecDeque<Vec<u8>>);
impl Broker {
/// Append message to internal buffer
async fn send(&mut self, topic: String, msg: &String) {
let meta: &String = self.get_or_request_metadata(&topic).await;
self.buffer.lock().await.add(msg, topic, meta);
}
async fn get_or_request_metadata(&mut self, _topic: &String) -> &String {
// Request metadata from TcpStream
unimplemented!()
}
}
impl Buffer {
fn add(&mut self, _msg: &String, _topic: String, _meta: &String) {
unimplemented!()
}
}
fn main() {}
error[E0502]: cannot borrow `self.buffer` as immutable because it is also borrowed as mutable
--> src/main.rs:21:9
|
20 | let meta = self.get_or_request_metadata(&topic).await;
| ---- mutable borrow occurs here
21 | self.buffer.lock().await.add(msg, topic, meta);
| ^^^^^^^^^^^ immutable borrow occurs here ---- mutable borrow later used here
What I can not understand, is why &mut self
has such long lifespan. Technically I do understand, that I have called get_or_request_metadata(&mut self, ...)
, but once call is complete, I've got immutable `meta: &String'. Why Rust thinks that there is a risk of mutation after call to mutating function is complete?
And what are idiomatic ways of dealing with situations like this, when you have to mutate self
to cache request results inside component? I was considering RefCell
but I would like to make sure there is no better way because runtime checks do not inspire confidence.