Rust can't modify RefCell in Rc - rust

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.

Related

borrowed data escapes outside of closure

I created two structs, Car and CarModifier. Both Car and CarModifier are instantiated in main.rs. CarModifier modifies one of the fields of Car using data from curl request. Here is the code:
main.rs
use sample::{car::Car, car_modifier::CarModifier};
fn main() {
println!("Hello, world!");
let mut car = Car::new();
let mut car_modifier = CarModifier::new();
car_modifier.update_car(&mut car);
println!("{:?}", car.driver);
}
Car.rs
#[derive(Debug)]
pub struct Car<'a> {
pub driver: &'a [u8]
}
impl<'a> Car<'a> {
pub fn new() -> Self {
Self {
driver: &[26]
}
}
pub fn update_driver(&mut self, new_driver: &'a [u8]) {
self.driver = new_driver;
}
}
CarModifier.rs
use curl::easy::Easy;
use crate::{car::Car};
pub struct CarModifier;
impl CarModifier {
pub fn new() -> Self {
Self {
}
}
pub fn update_car(&mut self, car: &mut Car) {
let mut easy = Easy::new();
easy.url("https://www.rust-lang.org/").unwrap();
let mut transfer = easy.transfer();
transfer.write_function(|data: &[u8]| {
car.update_driver(data);
Ok(data.len())
}).unwrap();
transfer.perform().unwrap();
}
}
This is the error which I get when trying to run
error[E0521]: borrowed data escapes outside of closure
--> src/car_modifier.rs:19:13
|
13 | pub fn update_car(&mut self, car: &mut Car) {
| --- `car` declared here, outside of the closure body
...
18 | transfer.write_function(|data: &[u8]| {
| ---- `data` is a reference that is only valid in the closure body
19 | car.update_driver(data);
| ^^^^^^^^^^^^^^^^^^^^^^^ `data` escapes the closure body here
For more information about this error, try `rustc --explain E0521`.
error: could not compile `sample` due to previous error
I seem to understand from the error that data lives only in the closure body and the code tries to refer that in car.driver which will outlive the closure body, hence the error. Is this the right understanding?
I am also aware that instead of using &[u8], I could use Vec<u8> to resolve the error. But how can I keep using &[u8]?
Basically data belongs to the curl library. It may be a slice of some library internal buffer pool, and may be re-used for some other curl api calls later on, or freed after this call. You need to copy data into the Car structure instead of just having a pointer to the underlying buffer. Rust compiler helps you to catch that problem. In C++ the compiler won't even tell you this error and the C++ code will have runtime problems.

Can this kind of pattern be implemented in Rust?

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

"borrowed value does not live long enough" after implementing Drop trait

Being new to rust I wanted to play with some data structures and ended up with something like a node type without payload.
use std::cell::RefCell;
use std::collections::HashMap;
use std::ops::Drop;
#[derive(Debug)]
struct Container<'a> {
next : Option<&'a RefCell<Container<'a>>>,
}
impl<'a> Container<'a> {
fn new() -> Container<'a> {
Container { next: None }
}
fn set(&mut self, next: &'a RefCell<Container<'a>>) {
self.next = Some(next);
}
}
The goal was to have these nodes not own their neighbours, so std::rc::Rc was out of the question.
So I did some testing which went fine:
fn main() {
// items:
let cont_1 = RefCell::new(Container::new());
let cont_2 = RefCell::new(Container::new());
let b_1 = &cont_1;
let b_2 = &cont_2;
(*b_2).borrow_mut().set(b_1);
(*b_1).borrow_mut().set(b_2);
println!("{:?}", b_1.borrow());
}
Since I was playing around I then tried to implement the Drop trait on the Container type
impl<'a> Drop for Container<'a>{
fn drop(&mut self) {}
}
which results in two of (the other one for cont_2)
error[E0597]: `cont_1` does not live long enough
--> src/main.rs:11:15
|
11 | let b_1 = &cont_1;
| ^^^^^^^ borrowed value does not live long enough
...
18 | }
| -
| |
| `cont_1` dropped here while still borrowed
| borrow might be used here, when `cont_1` is dropped and runs the destructor for type `std::cell::RefCell<Container<'_>>`
Now, I believe, that Drop causes the deallocation to be at the end of scopes otherwise it would usually take place after the last use? But either way the complaint is about the value not living long enough... I have tried adding drop(...) in between, but failed. I guess I dont even understand how exactly the order of deallocation changed. I would expect that cont_1 would be deallocated last since it was initialized/declared first meaning that I don't really understand how it could still be borrowed.
What would happen if in your drop() implementation you use self.next.unwrap()...? Since one of your variables will necessarily be dropped before the other, the last one will have a dangling reference, and so undefined behavior. So you code is correct in not to compile.
IMO, the solution is to use some kind of reference counted pointers. If you do not want Rc, because they do not own the neighbors (it will create a reference loop and thus leak your objects), you can use Weak references. Something like this (playground):
use std::cell::RefCell;
use std::ops::Drop;
use std::rc::{Rc, Weak};
#[derive(Debug)]
struct Container {
next : Option<Weak<RefCell<Container>>>,
}
impl Container {
fn new() -> Container {
Container { next: None }
}
fn set(&mut self, next: &Rc<RefCell<Container>>) {
self.next = Some(Rc::downgrade(next));
}
}
impl Drop for Container{
fn drop(&mut self) {}
}
fn main() {
// items:
let cont_1 = Rc::new(RefCell::new(Container::new()));
let cont_2 = Rc::new(RefCell::new(Container::new()));
cont_1.borrow_mut().set(&cont_1);
cont_2.borrow_mut().set(&cont_2);
println!("{:?}", cont_1.borrow());
}

Convert boxed trait to mutable trait reference in Rust

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

How do I pass Rc<RefCell<Box<MyStruct>>> to a function accepting Rc<RefCell<Box<dyn MyTrait>>>?

I have originally asked this question here, but it was marked as duplicate, although it duplicates only a part of it in my opinion, so I have created a more specific one:
Consider the following code:
use std::rc::Rc;
trait MyTrait {
fn trait_func(&self);
}
struct MyStruct1;
impl MyStruct1 {
fn my_fn(&self) {
// do something
}
}
impl MyTrait for MyStruct1 {
fn trait_func(&self) {
// do something
}
}
fn my_trait_fn(t: Rc<dyn MyTrait>) {
t.trait_func();
}
fn main() {
let my_str: Rc<MyStruct1> = Rc::new(MyStruct1);
my_trait_fn(my_str.clone());
my_str.my_fn();
}
This code works fine. Now I want to change the definition of trait_func to accept a &mut self, but it won't work as Rc works with immutable data only. The solution I use is to wrap MyTrait into RefCell:
use std::cell::RefCell;
fn my_trait_fn(t: Rc<RefCell<Box<dyn MyTrait>>>) {
t.borrow_mut().trait_func();
}
fn main() {
let my_str: Rc<RefCell<Box<MyStruct1>>> = Rc::new(RefCell::new(Box::new(MyStruct1)));
my_trait_fn(my_str.clone());
my_str.my_fn();
}
When I compile it I get an error:
error[E0308]: mismatched types
--> src/main.rs:27:17
|
27 | my_trait_fn(my_str.clone());
| ^^^^^^^^^^^^^^ expected trait MyTrait, found struct `MyStruct1`
|
= note: expected type `std::rc::Rc<std::cell::RefCell<std::boxed::Box<dyn MyTrait + 'static>>>`
found type `std::rc::Rc<std::cell::RefCell<std::boxed::Box<MyStruct1>>>`
= help: here are some functions which might fulfill your needs:
- .into_inner()
What's the best way to go around this problem?
(An older revision of this answer essentially advised to clone the underlying struct and put it in a new Rc<RefCell<Box<MyTrait>> object; this was necessary at the time on stable Rust, but since not long after that time, Rc<RefCell<MyStruct>> will coerce to Rc<RefCell<MyTrait>> without trouble.)
Drop the Box<> wrapping, and you can coerce Rc<RefCell<MyStruct>> to Rc<RefCell<MyTrait>> freely and easily. Recalling that cloning an Rc<T> just produces another Rc<T>, increasing the refcount by one, you can do something like this:
use std::rc::Rc;
use std::cell::RefCell;
trait MyTrait {
fn trait_func(&self);
}
#[derive(Clone)]
struct MyStruct1;
impl MyStruct1 {
fn my_fn(&self) {
// do something
}
}
impl MyTrait for MyStruct1 {
fn trait_func(&self) {
// do something
}
}
fn my_trait_fn(t: Rc<RefCell<MyTrait>>) {
t.borrow_mut().trait_func();
}
fn main() {
// (The type annotation is not necessary here, but helps explain it.
// If the `my_str.borrow().my_fn()` line was missing, it would actually
// be of type Rc<RefCell<MyTrait>> instead of Rc<RefCell<MyStruct1>>,
// essentially doing the coercion one step earlier.)
let my_str: Rc<RefCell<MyStruct1>> = Rc::new(RefCell::new(MyStruct1));
my_trait_fn(my_str.clone());
my_str.borrow().my_fn();
}
As a general rule, see if you can make things take the contained value by reference, ideally even generically—fn my_trait_fn<T: MyTrait>(t: &T) and similar, which can typically be called as my_str.borrow() with automatic referencing and dereferencing taking care of the rest—rather than the whole Rc<RefCell<MyTrait>> thing.

Resources