|
| 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