Different lifetimes of trait objects. Why?

Hello!

I'm new to rust and need your help with the following code. If I just comment out the line 33, it will compile. I don't see the real difference between declaration of a and b variables (lines 26 and 27 respectively). But lifetimes of this variables are different. Why? How is it possible to extend the lifetime of b to fix the problem?

Many thanks in advance!

use std::thread;

trait Trait: Send + Sync + 'static {
    fn foo(&self);
}

struct A;

impl Trait for A {
    fn foo(&self) {
        println!("I'm the struct A");
    }
}

struct B {
    name: String,
}

impl Trait for B {
    fn foo(&self) {
        println!("I'm the struct B. My name is {}.", self.name);
    }
}

fn main() {
    let a = &A {};
    let b = &B {
        name: "Bob".to_string(),
    };

    let mut vec: Vec<&dyn Trait> = Vec::new();
    vec.push(a);
    vec.push(b);

    let handler = thread::spawn(|| {
        for it in vec {
            it.foo();
        }
    });

    handler.join().unwrap();
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0716]: temporary value dropped while borrowed
  --> src/main.rs:27:14
   |
27 |       let b = &B {
   |  ______________^
28 | |         name: "Bob".to_string(),
29 | |     };
   | |_____^ creates a temporary which is freed while still in use
...
33 |       vec.push(b);
   |                - cast requires that borrow lasts for `'static`
...
42 |   }
   |   - temporary value is freed at the end of this statement

error: aborting due to previous error

For more information about this error, try `rustc --explain E0716`.
error: could not compile `playground`.

To learn more, run the command again with --verbose.

1 Like

Hi,
from my experience if you'd like to work with trait objects and you'd like to put them into a Vec the most common way is to Box them. This will store the trait objects on the heap and unifies them accross their implementing structures.

fn main() {
    let a = Box::new(A {});
    let b = Box::new(B {
        name: "Bob".to_string(),
    });

    let mut vec: Vec<Box<dyn Trait>> = Vec::new();
    vec.push(a);
    vec.push(b);

    let handler = thread::spawn(|| {
        for it in vec {
            it.foo();
        }
    });

    handler.join().unwrap();
}
1 Like

@2ndTaleStudio: Thank you very much for your help! :slight_smile:

The code is now functional. :partying_face:

But I still do not understand what is wrong with my code. The question about different lifetimes of a and b is not clarified. :frowning_face:

The difference is that &A {} compiles into a reference to an immutable global, whereas b is a reference to a stack variable that is destroyed at the end of main, and thus you can't move it into another thread as b borrows from main.

2 Likes

But why? Why is a stored as immutable global, but b stored in stack? Is it possible to enforce to store b as immutable global?

1 Like

It is because "Bob".to_string() is computed at runtime, whereas A {} is a valid constant expression. Use a Box instead of a reference as @2ndTaleStudio suggested.

In my opinion, the most confused is the fact, that declarations for both variables are identical, but lifetimes are different. Is this a result of some kind of rust's compiler optimizations or documented feature?

Documented here

https://doc.rust-lang.org/reference/expressions.html#temporary-lifetimes

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.