I want to implement the observer pattern using Weak smart pointers. Ideally this would look something like the following (playground):
use std::cell::RefCell;
use std::rc::{Rc, Weak};
struct Observable {
observers: Vec<Weak<Observer>>,
}
impl Observable {
fn new() -> RefCell<Observable> {
RefCell::new(Self {
observers: Vec::new(),
})
}
}
struct Observer<'a> {
parent: &'a RefCell<Observable>,
}
fn observe(observable: &RefCell<Observable>) -> Rc<Observer> {
let observer = Observer {
parent: observable,
};
let observer = Rc::new(observer);
observable.borrow_mut().observers.push(Rc::downgrade(&observer));
observer
}
fn main() {
let mut observable = Observable::new();
let observer = observe(&observable);
}
This fails however, because no lifetime parameter is provided for the Observer type in the observers field of Observable:
error[E0106]: missing lifetime specifier
--> src/main.rs:5:25
|
5 | observers: Vec<Weak<Observer>>,
| ^^^^^^^^ expected named lifetime parameter
|
help: consider introducing a named lifetime parameter
|
4 ~ struct Observable<'a> {
5 ~ observers: Vec<Weak<Observer<'a>>>,
|
For more information about this error, try `rustc --explain E0106`.
Is there a way in which this lifetime can be added to the observers field or removed from the Observer without causing issues with cyclic lifetime dependencies? Is there some way in which the Weakness of the pointer can apply to the lifetime of the Observer itself, as well as the lifetime of the reference?
Related
So, I have the following problem. I have a structure implementation, and one of the methods of this structure consumes the instance of the structure, which is ok by me, as it is literally the last thing I want to do in my program with this instance (my_struct), but it's totally not OK with the borrow checker:
use std::thread;
struct MyStruct {
some_field: Vec<String>
}
impl MyStruct {
fn new() -> Self {
MyStruct {
some_field: vec!("str".to_string())
}
}
fn do_something_with_self_in_thread(&'static mut self) {
thread::spawn(move || self.some_field = vec!("another_str".to_string()));
}
}
fn main() {
let my_struct: &'static mut MyStruct = &mut MyStruct::new();
my_struct.do_something_with_self_in_thread()
// Some blocking call will follow
}
Error:
error[E0716]: temporary value dropped while borrowed
--> src/main.rs:20:49
|
20 | let my_struct: &'static mut MyStruct = &mut MyStruct::new();
| --------------------- ^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use
| |
| type annotation requires that borrow lasts for `'static`
21 | my_struct.do_something_with_self_in_thread()
22 | }
| - temporary value is freed at the end of this statement
For more information about this error, try `rustc --explain E0716`.
I tried playing around with lifetimes, but resultless.
How do I get myself out of this situation?
Playground link
When we say a method consumes the instance, then it should take self and not a mutable borrow &mut self in the definition.
i.e.
impl MyStruct {
...
fn do_something_with_self_in_thread(self) {
...
}
}
As commenters have already said, your second problem is with lifetimes. The compiler can't reason about references not outliving owners of resources if they are in separate threads. So to this end the thread::spawn method has trait bounds requiring that the closure is 'static, which means the variables it captures can live as long as they like. Examples which are valid would be owned types T, not references &T or &mut T, unless they are &'static T types. That's not the case in your code as you've instantiated a struct inside main (albeit erroneously annotated as having a 'static lifetime) and then tried to move a mutable borrow of a field inside your closure.
I have a library that I'm writing that's entirely based around ownership. Mostly as an academic exercise, I'm looking at migrating it to use lifetimes. Given something like this,
#[derive(Default)]
struct Foo { test: i32 }
#[derive(Default)]
struct Bar { foo: Foo }
Now I can easily modify bar to use lifetimes,
#[derive(Default)]
struct Foo { test: i32 }
struct Bar<'a> { foo: &'a Foo }
But that'll trigger an error,
error[E0277]: the trait bound `&Foo: Default` is not satisfied
--> src/main.rs:6:18
|
5 | #[derive(Default)]
| ------- in this derive macro expansion
6 | struct Bar<'a> { foo: &'a Foo }
| ^^^^^^^^^^^^ the trait `Default` is not implemented for `&Foo`
|
= help: the trait `Default` is implemented for `Foo`
= note: this error originates in the derive macro `Default` (in Nightly builds, run with -Z macro-backtrace for more info)
For more information about this error, try `rustc --explain E0277`.
How can I get around this?
Default trait with borrowed types
The Default trait can only be automatically derived if all the types are owned. That's not to say you can't create your own implementation though. But the "default" reference must exist outside of the function to satisfy the borrow checker. Here we create a new default which needs to be constant and then we create a global instantiation of it,
impl Foo {
const fn new_const_default() -> Self {
// must repeat Default.
Self { u32: 0 }
}
}
// // Note you can use `new_const_default` to implement Default
// // This eliminates having two potentially conflicting defaults
// impl Default for Foo {
// fn default() -> Self { Self::new_const_default() }
// }
// Now we have a reference to a static global.
const GLOBAL_FOO: &'static Foo = &Bar::new_const_default();
impl<'a> Default for Bar<'a> {
fn default() -> Sequence<'a> {
Self {
foo: GLOBAL_FOO
}
}
}
User provided
Another often-found workaround which will place the reference to Foo outside of the scope of Bar's default is to require the user to send it in. Though this will change your API.
let foo = Foo::default();
let bar = Bar::new_with_foo( &foo );
Though note, this means Bar will not implement or satisfy Default.
I want to simulate some natural process, so I have a Simulator, and a reactor like a NuclearReactor. The simulator will modify the reactor, and the reactor can reversely influance the simulator by modifying it. One important thing is that the NuclearReactor is wrapped from somewhere else, the solid_function must has a inmutable &self.
So after reading rust book of RefCell, I wrote something like these:
use std::borrow::BorrowMut;
use std::cell::RefCell;
use std::rc::{Rc, Weak};
pub struct Simulator {
nr: NuclearReactor,
data: Vec<f64>,
}
impl Simulator {
pub fn on_nuclear_data(&mut self, x: i64) {
// modify self
}
pub fn run_simulation(&mut self) {}
}
pub struct NuclearReactor {
simulator: Option<Weak<RefCell<Simulator>>>,
}
impl NuclearReactor {
pub fn solid_function(&self, x: i64) {
/*
this function `&self` is solid, so I have to use a RefCell to wrap Simulator
*/
}
pub fn write_simulator(&self) {
/*
none of the two following snippets will work
*/
/* snippet1: compiler says:
error[E0507]: cannot move out of an `Rc`
--> src/main.rs:87:17
|
87 | let t = *self.simulator.unwrap().upgrade().unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| move occurs because value has type `RefCell<Simulator>`, which does not implement the `Copy` trait
| help: consider borrowing here: `&*self.simulator.unwrap().upgrade().unwrap()`
*/
let t = *self.simulator.unwrap().upgrade().unwrap();
t.borrow_mut().on_nuclear_data(0);
/*
snippet2: compiler says:
error[E0599]: no method named `on_nuclear_data` found for mutable reference `&mut Rc<RefCell<Simulator>>` in the current scope
--> src/main.rs:101:65
|
101 | self.simulator.unwrap().upgrade().unwrap().borrow_mut().on_nuclear_data(0);
| ^^^^^^^^^^^^^^^ method not found in `&mut Rc<RefCell<Simulator>>`
*/
// self.simulator.unwrap().upgrade().unwrap().borrow_mut().on_nuclear_data(0);
}
}
pub fn main() {
let nr_ = NuclearReactor {
simulator: None
};
let mut sm_ = Rc::new(RefCell::new(Simulator {
nr: nr_,
data: vec![],
}));
(*sm_).borrow_mut().nr.simulator = Some(Rc::downgrade(&sm_));
}
But it won't compile. How should I solve it.
Oh... the compile hints me solved it. But it seems complicated. Can anyone explain that, or give a better pattern? I think the pattern is common.
let t = &*self.simulator.as_ref().unwrap().upgrade().unwrap();
t.borrow_mut().on_nuclear_data(0);
Actually it should be:
(*self.simulator.as_ref().unwrap().upgrade().unwrap()).borrow_mut().on_nuclear_data(0);
This is because &Option<T> cannot be unwrapped.
self.simulator, where self is a &Self, gets a &Option<Weak<RefCell<Simulator>>>.
Option<T>::unwrap consumes the self and return the inner value by 'move'.
Option<T>::as_ref converts &self into an Option<&T> safely so that you can unwrap into a &T.
I am writing a tool that generates Rust code, and I've come up with the following pattern that is being generated:
pub fn my_template() {
let mut o = HtmlGenerator::new();
o.paragraph(&mut || {
o.emphasis(&mut || {
o.normal_text("hello");
});
});
}
The paragraph and emphasis methods have the type:
fn paragraph(&mut self, cnt: &mut impl FnMut());
I get the following error:
|
197 | o.paragraph(&mut || {
| ^ --------- -- first mutable borrow occurs here
| | |
| _________| first borrow later used by call
| |
198 | | o.normal_text("hello");
| | - first borrow occurs due to use of `o` in closure
199 | | });
| |__________^ second mutable borrow occurs here
Apparently I cannot borrow the mutable reference twice. It needs to be mutable because some of the generator functions might change the state of the HtmlGenerator, which is why I use methods in the first place.
Can this pattern be implemented in Rust? Am I trying to create nonsense?
The pattern cannot be implemented with exactly the provided signatures of paragraph() and other methods. But it's possible to get the same effect with just minor changes to either the closure signature or the method signatures.
The simplest and cleanest solution would be to pass the HtmlGenerator to the closures:
struct HtmlGenerator {
data: String,
}
impl HtmlGenerator {
fn new() -> HtmlGenerator {
HtmlGenerator {
data: "".to_string(),
}
}
fn paragraph(&mut self, inside: impl FnOnce(&mut HtmlGenerator)) {
self.data.push_str("<p>");
inside(self);
self.data.push_str("</p>");
}
fn normal_text(&mut self, text: &str) {
self.data.push_str(text);
}
fn into_data(self) -> String {
self.data
}
}
Playground
An alternative is to leave the closure signature as-is, but use interior mutability to make methods like paragraph() accept &self, at a very tiny run-time cost. For example:
struct HtmlGenerator {
data: RefCell<String>,
}
impl HtmlGenerator {
fn new() -> HtmlGenerator {
HtmlGenerator {
data: RefCell::new("".to_string()),
}
}
fn paragraph(&self, inside: impl FnOnce()) {
self.data.borrow_mut().push_str("<p>");
inside();
self.data.borrow_mut().push_str("</p>");
}
fn normal_text(&self, text: &str) {
self.data.borrow_mut().push_str(text);
}
fn into_data(self) -> String {
self.data.into_inner()
}
}
Playground
It may appear wrong to modify the data from an "immutable" method, but &self just means that the data is safe to share. self.data.borrow_mut() will panic if the borrow is already active, but that can't happen in the above code because the borrow is released as soon as the underlying data is modified. If we were to hold on to the guard returned by borrow_mut() and invoke the closure with the guard object live, the inner borrow_mut() in normal_text() invoked by the closure would panic.
In both approaches the trait bound for the closures can be the most permissive FnOnce, as the methods only invoke them once.
My question now is whether this pattern can even be ipmlemented in rust or whether I'm trying to create nonsense here?
It can't work like this, because the closure closes over the generator in order to implement the inner call, so the closure and the caller's borrow will necessarily overlap, which can not work.
What can work is for the generator methods to pass in the generator instance to the closures they call:
pub fn my_template() {
let mut o = HtmlGenerator::new();
o.paragraph(|o: &mut HtmlGenerator| {
o.emphasis(|o: &mut HtmlGenerator| {
o.normal_text("hello");
});
});
or something like that.
I also think you're probably better off passing the callable by value:
fn paragraph(&mut self, mut cnt: impl FnMut());
I am having some trouble with the dynamic dispatch pointer types in Rust. I want to convert a value of type Box<MyTrait> to &mut MyTrait to pass to a function. For example, I tried:
use std::borrow::BorrowMut;
trait MyTrait {
fn say_hi(&mut self);
}
struct MyStruct { }
impl MyTrait for MyStruct {
fn say_hi(&mut self) {
println!("hi");
}
}
fn invoke_from_ref(value: &mut MyTrait) {
value.say_hi();
}
fn main() {
let mut boxed_trait: Box<MyTrait> = Box::new(MyStruct {});
invoke_from_ref(boxed_trait.borrow_mut());
}
This fails with the following error:
error: `boxed_trait` does not live long enough
--> <anon>:22:5
|
21 | invoke_from_ref(boxed_trait.borrow_mut());
| ----------- borrow occurs here
22 | }
| ^ `boxed_trait` dropped here while still borrowed
|
= note: values in a scope are dropped in the opposite order they are created
Strangely enough, this works for &MyTrait but not for &mut MyTrait. Is there any way I can get this conversion to work in the mutable case?
I think you're running into a limitation of the current compiler's lifetime handling. borrow_mut, being a function, imposes stricter lifetime requirements than necessary.
Instead, you can take a mutable borrow to the box's interior by first dereferencing the box, like this:
fn main() {
let mut boxed_trait: Box<MyTrait> = Box::new(MyStruct {});
invoke_from_ref(&mut *boxed_trait);
}