Why does this code compile?

I have created this minimal example which encapsulates the crux I'm trying to wrap my head around.

use std::any::Any;

trait Trait {}

struct Struct {}

impl Trait for Struct {}

struct Container {
    field: Option<Box<dyn Any>>,
}

impl Container {
    fn set_field<T: Any>(&mut self, x: T) {
        self.field = Some(Box::new(x));
    }
}

fn main() {
    let c = Container { field: None };
    {
        let mut s = Struct {};
        setter(c, &mut s);
    }
    println!("no s anymore!");
}

fn setter(mut c: Container, t: &mut (dyn Trait + 'static)) {
    c.set_field(t as *mut (dyn Trait));
}

This code only compiles if the setter method has the 'static life time bound for the t argument. But what I pass to this method above is a mutable reference to a struct, which has clearly a non-static lifetime. So why does this code even compile?

Thanks a lot!

Struct is always 'static (this type doesn't have any generic lifetime parameters). + 'static in dyn affects dyn Trait, not the reference.

A reference to Struct is not necessarily so, but requiring a reference to be 'static would be written like &'static mut dyn Trait.

2 Likes

Thank you so much! What you're saying makes sense, but now my question boils down to why Struct is always 'static? I cannot find this to be specified anywhere, and even the opposite — I find only description of 'static as being the lifetime of the entire program (https://doc.rust-lang.org/1.30.0/book/first-edition/lifetimes.html), which is clearly not the case for my struct s? (that's why I deliberately gave it an explicit scope in my example)

Struct is 'static because it doesn't borrow anything. You can move Struct anywhere without having to worry about borrow-checker.

The lifetime in dyn Trait + 'a is specifically for structs that borrow. For instance, consider the following example:

use std::fmt::Debug;

#[derive(Debug)]
struct X<'a> {
    x: &'a i32,
}

fn add_vec<'a>(v: &mut Vec<Box<dyn Debug + 'a>>, y: Box<dyn Debug + 'a>) {
    v.push(y);
}

fn main() {
    let vec = {
        let x = 42;
        let mut vec = Vec::new();
        add_vec(&mut vec, Box::new(X { x: &x }));
        vec
    };
    // Not allowed, `x` already dropped
    println!("{:?}", vec);
}

Here, X is not 'static (unless it's X<'static>).

If a structure doesn't borrow anything and only owns, it's 'static, as it doesn't need to concern itself with lifetimes.

2 Likes

To add to this, the code will all still be valid if Struct can contain 'static references:

struct Struct {i: &'static str}

References to 'static data are always valid.


Even a reference to data which should have been dropped is valid with Any, though. Any seems to be smart enough to recognize forgotten references.

use std::any::Any;

trait Trait {}

impl Trait for Vec<i32> {}

struct Container {
    field: Box<dyn Any>,
}

impl Container {
    fn set_field<T: Any>(&mut self, x: T) {
        self.field = Box::new(x);
    }
}

fn main() {
    let mut c = Container { field: Box::new(1) };

    {
        let mut s: Vec<i32> = vec![1];
        setter(&mut c, &mut s)
    }
    dbg!(c.field.downcast_ref::<&mut Vec<i32>>());
}

fn setter(c: &mut Container, t: &mut (dyn Trait + 'static)) {
    c.set_field(t as *mut (dyn Trait));
}

Why does this work?

That's not a mutable reference anymore. It's a raw pointer, and Rust doesn't track lifetimes of raw pointers (so they satisfy the 'static requirement).

1 Like