This can be achieved in two ways. Using smart-pointers or introducing internal mutability to the ThingMaker
.
Smart-pointers
Using this approach we wrap our reference to ThingMaker
in Rc<RefCell>
so that we can have multiple mutable references to one object. There is a limitation to this method (correct me if I'm wrong) - you cannot easily make new Thing
s from inside the ThingMaker
s methods. That is because we cannot easily obtain the Rc
reference to an object from inside said object. Note that neither cells nor Rc
are thread safe, so if you have a multi-threaded environment use Arc
instead of Rc
and Mutex
instead of cells. Here is an example using Rc<RefCell>
smart-pointer. (thanks @eggyal)
In this expample, we rewrote Thing
to store a smart-pointer instead of a direct reference:
struct Thing {
pub parent: Rc<RefCell<ThingMaker>>,
}
we then have to wrap our maker in a Rc<RefCell>
smart-pointer, like this:
let maker = Rc::new(RefCell::new(ThingMaker { data: 16 }));
Which we then give to creation function to make Thing
s:
ThingMaker::make_thing(&maker);
Later, we can access and mutate ThingMaker
using a Thing
s reference like so:
let mut parent = thing.parent.borrow_mut();
parent.data += 1;
Rc
smart-pointer allows shared ownership of ThingMaker
between Thing
s, and the RefCell
allows us to reintroduce mutability, seeing as Rc
is an immutable pointer.
Interior mutability
This method makes it so that we do not need a mutable reference to mutate our object, only an immutable one. As we can have as many immutable referenes as we want, we can make as many Thing
s as we want. To achieve this we can use Cells
and RefCell
s (refer to documentation to see which one will be better in you case). Additionally, if you are in a multi-threaded environment, you can use Mutex
, as cells are not thread safe. Here is an example of using RefCell
to introduce intrerior mutability. (thanks @kmdreko)
As you can see, the data of our ThingMaker
was wrapped in a RefCell
:
struct ThingMaker {
data: RefCell<u64>,
}
Which we can then use to get mutable references to our data:
fn process_thing(obj: &Thing) {
let mut data = &mut *obj.parent.data.borrow_mut();
*data += 1;
println!("New data: {}", data);
}
Note that we only need an immutable reference to mutate our object, so we can get away with only storing immutable references inside our Thing
s:
struct Thing<'a> {
pub parent: &'a ThingMaker,
}