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 Things from inside the ThingMakers 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 Things:
ThingMaker::make_thing(&maker);
Later, we can access and mutate ThingMaker using a Things reference like so:
let mut parent = thing.parent.borrow_mut();
parent.data += 1;
Rc smart-pointer allows shared ownership of ThingMaker between Things, 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 Things as we want. To achieve this we can use Cells and RefCells (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 Things:
struct Thing<'a> {
pub parent: &'a ThingMaker,
}