Rick de Water
Rick de Water

Reputation: 2642

How to ensure that an object does not outlive its factory

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

Answers (2)

Jonathan Giddy
Jonathan Giddy

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

kmdreko
kmdreko

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 Objects 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

Related Questions