Reputation: 2642
Lets say I have a factory of objects:
struct Object;
struct Factory;
impl Object {
pub fn new() -> Object { Object{} }
}
impl Factory {
pub fn new() -> Factory { Factory{} }
pub fn create() -> Object { Object::new() }
}
How can you ensure that objects created by the factory do no outlive the factory? In other words, how to make code like this not compile:
let factory = Factory::new();
let object = factory.create();
drop(factory); // Some kind of lifetime compile error
Feel free to use any wrappers/containers to keep track of everything, as long as the compiler knows that something's up. I have tried returning a lot of different things myself, including &RefCell<Object>
, but I always run into a wall and end up starting over.
Upvotes: 2
Views: 128
Reputation: 1831
Keep a reference to the factory in the object. This will make it impossible to drop (or mutate) factory
before its outstanding objects are dropped.
The code below fails with a lifetime error because it tries to make object
outlive factory
:
struct Object<'a> {
factory: &'a Factory
}
struct Factory;
impl<'a> Object<'a> {
pub fn new(factory: &Factory) -> Object { Object{factory} }
}
impl Factory {
pub fn new() -> Factory { Factory{} }
pub fn create(&self) -> Object { Object::new(self) }
}
fn main() {
let _object = {
let factory = Factory::new();
let object = factory.create();
object
};
}
Upvotes: 1
Reputation: 60503
Rust has a lifetime/borrow-checking mechanism to ensure references do not outlive the referenced object. You can use the same principles to guarantee that Object
s do not outlive their Factory
. All you need is for Object
to have a lifetime annotation that is bound to the Factory
like so (playground):
#[derive(Debug)]
struct Factory;
#[derive(Debug)]
struct Object<'a> {
// You can keep a reference to the factory if that's useful
factory: &'a Factory,
// Or you can save the bytes by using PhantomData instead
// dummy: PhantomData<&'a Factory>,
}
impl Object<'_> {
pub fn new(factory: &Factory) -> Object<'_> { Object { factory } }
}
impl Factory {
pub fn new() -> Factory { Factory {} }
pub fn create(&self) -> Object<'_> { Object::new(self) }
}
fn main() {
let factory = Factory::new();
let object = factory.create();
drop(factory);
// Just dropping the factory wouldn't cause an error unless the Object had
// a Drop implementation or was used after the factory was dropped, so
// trying to "use" the object by printing it after the factory was dropped
// won't be allowed
println!("{:?}", object);
}
error[E0505]: cannot move out of `factory` because it is borrowed
--> src/main.rs:25:10
|
24 | let object = factory.create();
| ---------------- borrow of `factory` occurs here
25 | drop(factory);
| ^^^^^^^ move out of `factory` occurs here
26 |
27 | println!("{:?}", object);
| ------ borrow later used here
Upvotes: 4