Struggle with Rust compiler — 8 Link to heading

lifetime of dyn 2 Link to heading

In the previous story, I discussed compiler error associated with trait lifetime. In particular, we saw that

Box<dyn Trait>

is implicitly converted to

Box<dyn Trait + 'static>

In the previous example, all the trait objects I used indeed satisfied static lifetime, but what if we want to relax lifetime constraint of the Weapon trait object to non-static? (Rust Playground link)

trait Weapon {
    fn fire(&self);
}

struct Player {
    weapon: Box<dyn Weapon>,
}

impl Player {
    fn new(weapon: impl Weapon + 'static) -> Self {
        Self { weapon: Box::new(weapon) }
    }

    fn change_weapon(&mut self, weapon: impl Weapon + 'static) {
        self.weapon = Box::new(weapon);
    }

    fn shoot(&self) {
        self.weapon.fire();
    }
}

struct CustomWeapon<'a> {
    sound: &'a str
}

impl<'a> CustomWeapon<'a> {
    fn new(sound: &'a str) -> Self {
        Self { sound }
    }
}

impl<'a> Weapon for CustomWeapon<'a> {
    fn fire(&self) {
        println!("{}", self.sound)
    }
}


fn main() {
    let impact = String::from("pew pew"); // non-static
    let player = Player::new(CustomWeapon::new(&impact));
    player.shoot(); // pew pew
}

The difference from the previous example is that now CustomWeapon has a reference to non-static object.

error[E0597]: `impact` does not live long enough
  --> src/main.rs:42:48
   |
41 |     let impact = String::from("pew pew");
   |         ------ binding `impact` declared here
42 |     let player = Player::new(CustomWeapon::new(&impact));
   |                  ------------------------------^^^^^^^--
   |                  |                             |
   |                  |                             borrowed value does not live long enough
   |                  argument requires that `impact` is borrowed for `'static`
43 |     player.shoot(); // pew pew
44 | }
   | - `impact` dropped here while still borrowed

For more information about this error, try `rustc --explain E0597`.
error: could not compile `playground` (bin "playground") due to previous error

As expected, the compiler complains that impact object is not static. The fix is to introduce lifetime generic and replace 'static:

--- before
+++ after
@@ -5,2 +5,2 @@
-struct Player {
-    weapon: Box<dyn Weapon>,
+struct Player<'a> {
+    weapon: Box<dyn Weapon + 'a>,
@@ -9,2 +9,2 @@
-impl Player {
-    fn new(weapon: impl Weapon + 'static) -> Self {
+impl<'a> Player<'a> {
+    fn new(weapon: impl Weapon + 'a) -> Self {
@@ -14 +14 @@
-    fn change_weapon(&mut self, weapon: impl Weapon + 'static) {
+    fn change_weapon(&mut self, weapon: impl Weapon + 'a') {

With this change, the Player struct can take not only static but also non-static Weapon trait objects!