1

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`.
dspyz
  • 5,280
  • 2
  • 25
  • 63
  • nit: `Any + 'static` is redundant since `trait Any: 'static` – cafce25 Apr 21 '23 at 18:45
  • I just started tacking `'static` onto everything everywhere trying to get the compiler to stop complaining – dspyz Apr 21 '23 at 18:49
  • i'd argue it's a bad habit to try and silence the compiler. if we start doing that, rust becomes no better or safer than c. – Robert Cotterman Apr 22 '23 at 03:10
  • @RobertCotterman While it's clearly a bad idea to "silence the compiler" without understanding what one is doing, the reason you give is untrue. Safe Rust will not allow unsound code to compile, even regardless of whether the author understands annotations like `'static` or not. More importantly, almost everyone who wrote more than "hello world" in Rust has been guilty of doing so at one point or another. A novice shouldn't feel guilty for occasionally doing the same. – user4815162342 Apr 22 '23 at 09:01
  • @RobertCotterman I don't do that in real code. The point here was to narrow down the possibilities for where the complaint was coming from. There's a difference between what MWE-for-StackOverflow code and actual production code look like. When you want to isolate something weird, sticking annotations on everything everywhere is great (as long as you delete it when done) – dspyz May 23 '23 at 02:03

1 Answers1

3

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

cafce25
  • 15,907
  • 4
  • 25
  • 31