Search code examples
rustclonelifetimetrait-objects

Why can't I clone a `Vec` of cloneable constructors?


I have a structure wrapping a Vec of Box<dyn Any> constructors which I've asserted to be clone.

I have a trait Ctor which takes the clone logic and produces another Box<dyn Ctor>. It compiles fine if I don't implement the trait or if I don't call the clone function.

But if I do both, I get a weird message claiming data escapes its lifetime bounds even though I've asserted the value is 'static.

(The code is much easier to read than the above description but stackoverflow complains if you don't have enough english text in your post and nitpickers complain if you put lorem ipsum in your stackoverflow posts)

Uncommenting either block below compiles fine, but uncommenting both blocks simultaneously results in an error:

use std::any::Any;

// pub struct SVConstructor {
//     ctors: Vec<Box<dyn Ctor + 'static>>,
// }

// impl Clone for SVConstructor {
//     fn clone(&self) -> Self {
//         let Self { ctors } = self;
//         let mut new_ctors: Vec<Box<dyn Ctor + 'static>> = Vec::new();
//         for ctor in ctors {
//             let value: Box<dyn Ctor + 'static> = ctor.clone_box();
//             drop(ctor);
//             new_ctors.push(value);
//         }
//         Self { ctors: new_ctors }
//     }
// }

pub trait Ctor: Fn() -> Box<dyn Any + Send + Sync + 'static> + Send + Sync {
    fn clone_box(&self) -> Box<dyn Ctor + 'static>;
}

// impl<T: Fn() -> Box<dyn Any + Send + Sync + 'static> + Send + Sync + Clone + 'static> Ctor for T {
//     fn clone_box(&self) -> Box<dyn Ctor + 'static> {
//         Box::new(self.clone())
//     }
// }

When I uncomment everything I get:

error[E0521]: borrowed data escapes outside of associated function
  --> src/lib.rs:12:50
   |
8  |     fn clone(&self) -> Self {
   |              -----
   |              |
   |              `self` is a reference that is only valid in the associated function body
   |              let's call the lifetime of this reference `'1`
...
12 |             let value: Box<dyn Ctor + 'static> = ctor.clone_box();
   |                                                  ^^^^^^^^^^^^^^^^
   |                                                  |
   |                                                  `self` escapes the associated function body here
   |                                                  argument requires that `'1` must outlive `'static`

For more information about this error, try `rustc --explain E0521`.

Solution

  • The problem is that ctors is of type &Vec<Box<dyn Ctor + 'static>> in the for loop and &Box<dyn Ctor + 'static> also implements Fn() -> Box<dyn Any + Send + Sync> + Send + Sync (the same Fn trait is implemented for both Box<F> and &F) and Clone (all references are Copy) so you're calling clone_box with T of &Box<dyn Ctor + 'static> and that leads to you cloning the reference along with it's lifetime instead of cloning the Ctor inside the Box. The solution is to get rid of that extra reference:

    impl Clone for SVConstructor {
        fn clone(&self) -> Self {
            let Self { ctors } = self;
            let mut new_ctors: Vec<Box<dyn Ctor + 'static>> = Vec::new();
            for ctor in ctors {
                let value: Box<dyn Ctor + 'static> = (*ctor).clone_box();
                new_ctors.push(value);
            }
            Self { ctors: new_ctors }
        }
    }
    

    Because ctor is only a reference the drop(ctor) doesn't do anything either so you can remove it as well.
    Playground