summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_error_codes/src/error_codes/E0772.md
blob: 5ffffd5112d38dff31049a1ae39e4aa97a8e0951 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#### Note: this error code is no longer emitted by the compiler.

A trait object has some specific lifetime `'1`, but it was used in a way that
requires it to have a `'static` lifetime.

Example of erroneous code:

```compile_fail
trait BooleanLike {}
trait Person {}

impl BooleanLike for bool {}

impl dyn Person {
    fn is_cool(&self) -> bool {
        // hey you, you're pretty cool
        true
    }
}

fn get_is_cool<'p>(person: &'p dyn Person) -> impl BooleanLike {
    // error: `person` has an anonymous lifetime `'p` but calling
    //        `print_cool_fn` introduces an implicit `'static` lifetime
    //        requirement
    person.is_cool()
}
```

The trait object `person` in the function `get_is_cool`, while already being
behind a reference with lifetime `'p`, also has it's own implicit lifetime,
`'2`.

Lifetime `'2` represents the data the trait object might hold inside, for
example:

```
trait MyTrait {}

struct MyStruct<'a>(&'a i32);

impl<'a> MyTrait for MyStruct<'a> {}
```

With this scenario, if a trait object of `dyn MyTrait + '2` was made from
`MyStruct<'a>`, `'a` must live as long, if not longer than `'2`. This allows the
trait object's internal data to be accessed safely from any trait methods. This
rule also goes for any lifetime any struct made into a trait object may have.

In the implementation for `dyn Person`, the `'2` lifetime representing the
internal data was omitted, meaning that the compiler inferred the lifetime
`'static`. As a result, the implementation's `is_cool` is inferred by the
compiler to look like this:

```
# trait Person {}
#
# impl dyn Person {
fn is_cool<'a>(self: &'a (dyn Person + 'static)) -> bool {unimplemented!()}
# }
```

While the `get_is_cool` function is inferred to look like this:

```
# trait Person {}
# trait BooleanLike {}
#
fn get_is_cool<'p, R: BooleanLike>(person: &'p (dyn Person + 'p)) -> R {
    unimplemented!()
}
```

Which brings us to the core of the problem; the assignment of type
`&'_ (dyn Person + '_)` to type `&'_ (dyn Person + 'static)` is impossible.

Fixing it is as simple as being generic over lifetime `'2`, as to prevent the
compiler from inferring it as `'static`:

```
# trait Person {}
#
impl<'d> dyn Person + 'd {/* ... */}

// This works too, and is more elegant:
//impl dyn Person + '_ {/* ... */}
```

See the [Rust Reference on Trait Object Lifetime Bounds][trait-objects] for
more information on trait object lifetimes.

[trait-object-lifetime-bounds]: https://doc.rust-lang.org/reference/types/trait-object.html#trait-object-lifetime-bounds