|  | 
|  | 1 | +A trait object has some specific lifetime `'1`, but it was used in a way that | 
|  | 2 | +requires it to have a `'static` lifetime. | 
|  | 3 | + | 
|  | 4 | +Example of erroneous code: | 
|  | 5 | + | 
|  | 6 | +```compile_fail,E0772 | 
|  | 7 | +trait BooleanLike {} | 
|  | 8 | +trait Person {} | 
|  | 9 | +
 | 
|  | 10 | +impl BooleanLike for bool {} | 
|  | 11 | +
 | 
|  | 12 | +impl dyn Person { | 
|  | 13 | +    fn is_cool(&self) -> bool { | 
|  | 14 | +        // hey you, you're pretty cool | 
|  | 15 | +        true | 
|  | 16 | +    } | 
|  | 17 | +} | 
|  | 18 | +
 | 
|  | 19 | +fn get_is_cool<'p>(person: &'p dyn Person) -> impl BooleanLike { | 
|  | 20 | +    // error: `person` has an anonymous lifetime `'p` but calling | 
|  | 21 | +    //        `print_cool_fn` introduces an implicit `'static` lifetime | 
|  | 22 | +    //        requirement | 
|  | 23 | +    person.is_cool() | 
|  | 24 | +} | 
|  | 25 | +``` | 
|  | 26 | + | 
|  | 27 | +The trait object `person` in the function `get_is_cool`, while already being | 
|  | 28 | +behind a reference with lifetime `'p`, also has it's own implicit lifetime, | 
|  | 29 | +`'2`. | 
|  | 30 | + | 
|  | 31 | +Lifetime `'2` represents the data the trait object might hold inside, for | 
|  | 32 | +example: | 
|  | 33 | + | 
|  | 34 | +``` | 
|  | 35 | +trait MyTrait {} | 
|  | 36 | +
 | 
|  | 37 | +struct MyStruct<'a>(&'a i32); | 
|  | 38 | +
 | 
|  | 39 | +impl<'a> MyTrait for MyStruct<'a> {} | 
|  | 40 | +``` | 
|  | 41 | + | 
|  | 42 | +With this scenario, if a trait object of `dyn MyTrait + '2` was made from | 
|  | 43 | +`MyStruct<'a>`, `'a` must live as long, if not longer than `'2`. This allows the | 
|  | 44 | +trait object's internal data to be accessed safely from any trait methods. This | 
|  | 45 | +rule also goes for any lifetime any struct made into a trait object may have. | 
|  | 46 | + | 
|  | 47 | +In the implementation for `dyn Person`, the `'2` lifetime representing the | 
|  | 48 | +internal data was ommitted, meaning that the compiler inferred the lifetime | 
|  | 49 | +`'static`. As a result, the implementation's `is_cool` is inferred by the | 
|  | 50 | +compiler to look like this: | 
|  | 51 | + | 
|  | 52 | +``` | 
|  | 53 | +# trait Person {} | 
|  | 54 | +# | 
|  | 55 | +# impl dyn Person { | 
|  | 56 | +fn is_cool<'a>(self: &'a (dyn Person + 'static)) -> bool {unimplemented!()} | 
|  | 57 | +# } | 
|  | 58 | +``` | 
|  | 59 | + | 
|  | 60 | +While the `get_is_cool` function is inferred to look like this: | 
|  | 61 | + | 
|  | 62 | +``` | 
|  | 63 | +# trait Person {} | 
|  | 64 | +# trait BooleanLike {} | 
|  | 65 | +# | 
|  | 66 | +fn get_is_cool<'p, R: BooleanLike>(person: &'p (dyn Person + 'p)) -> R { | 
|  | 67 | +    unimplemented!() | 
|  | 68 | +} | 
|  | 69 | +``` | 
|  | 70 | + | 
|  | 71 | +Which brings us to the core of the problem; the assignment of type | 
|  | 72 | +`&'_ (dyn Person + '_)` to type `&'_ (dyn Person + 'static)` is impossible. | 
|  | 73 | + | 
|  | 74 | +Fixing it is as simple as being generic over lifetime `'2`, as to prevent the | 
|  | 75 | +compiler from inferring it as `'static`: | 
|  | 76 | + | 
|  | 77 | +``` | 
|  | 78 | +# trait Person {} | 
|  | 79 | +# | 
|  | 80 | +impl<'d> dyn Person + 'd {/* ... */} | 
|  | 81 | +
 | 
|  | 82 | +// This works too, and is more elegant: | 
|  | 83 | +//impl dyn Person + '_ {/* ... */} | 
|  | 84 | +``` | 
|  | 85 | + | 
|  | 86 | +See the [Rust Reference on Trait Object Lifetime Bounds][trait-objects] for | 
|  | 87 | +more information on trait object lifetimes. | 
|  | 88 | + | 
|  | 89 | +[trait-object-lifetime-bounds]: https://doc.rust-lang.org/reference/types/trait-object.html#trait-object-lifetime-bounds | 
0 commit comments