RFCs 599 and 1156 specified our "object lifetime default" rules. These rules specify the default lifetime bound for a dyn Trait type.
The current rules specify the default as being a function of the immediate location in which the dyn Trait type appears. If the dyn Trait is the full type, then the default is 'static. Otherwise, if you have a &dyn Trait, then the default bound is the lifetime of the reference: so &'x dyn Trait defaults to &'x (dyn Trait + 'x). If the dyn Trait appears as an argument to a named struct/enum/trait (e.g., Box<dyn Trait> or Arc<dyn Trait>), then the default is usually 'static -- but the rules are a bit more subtle.
If the struct is declared with a single, explicit lifetime bound, such as:
struct Foo<'a, T: 'a> { }
// ^^^^^ explicit boundthen the default comes from that bound. Therefore, Foo<'x, dyn Trait> defaults to Foo<'x, dyn Trait + 'x>. This was intended to permit structs that act like & references (e.g., the Ref struct).
These same defaulting rules apply to trait types. So if you have dyn Foo<'x, dyn Bar> where Foo is declared like so:
trait Foo<'x, T: 'x> { .. }then the dyn Bar will default to dyn Bar + 'x. The same is true if you have the T: 'x bound in a where-clause:
trait Foo<'x, T>
where T: 'x
{ .. }The implementation however had an oversight: dyn Trait types can have not only regular type parameteres, but also associated type bindings. For example, one might have dyn Iterator<Item = dyn Bar>. This is pretty unusual, since most associated types must be Sized, but it is possible (that particular example, with Iterator, would be an error for this reason). In this case, the code defaulted to the behavior from RFC 599 (before the behavior was amended in RFC 1156) -- the default is "inherited" by looking further out at the surrounding context. So &'x dyn Foo<Item = dyn Bar> would default dyn Bar to dyn Bar + 'x (example). This behavior is (in my opinion) clearly a bug -- although RFC 1156 did not address this case specifically (an oversight), the intention of that RFC was expressly to reverse this "long distance" defaulting in favor of only considering the more immediate context.
To add to the complexity, we also support impl Trait types now -- these didn't exist when the original RFCs were written. Their defaulting rules presumably ought to be the same as dyn Trait types, but the current implementation doesn't treat them in quite the same way. For example, &impl Foo<Item = dyn Bar> winds up being roughly equivalent to dyn Bar + 'static (example).
In my opinion, the most natural rule would be to extend the rules for type arguments to associated types. For example, given a trait like Iterator (which has no lifetime parameters), the default bound for Item would be 'static (although, as Item: Sized, this is a moot point). But given a trait like so, where Self::Item: 'a:
trait RefTrait<'a> {
type Item: 'a;
}the default RefTrait<'x, Item = dyn Trait> would be dyn Trait + 'x.
Effect of PR 63376
The PR moves closer to the proposed rules but doesn't quite get there. In particular, I didn't try to figure out a case like RefTrait, but instead only to be forward compatible with it. This works by detecting that, if we have a trait with lifetime parameters, we have no object-lifetime default and require an explicit anotation. So users would be able to do dyn Foo<Item = dyn Bar> and have that default (correctly) to dyn Bar + 'static. But dyn RefTrait<'x, Item = dyn Trait> would be an error: you must write Item = dyn Trait + 'x explicitly.
The reason for not doing the full implementation is primarily that I wanted a smaller PR we could land sooner, so as to fix the current situation. The full changes are a bit more complex to get right.