-
Notifications
You must be signed in to change notification settings - Fork 532
Default trait object lifetimes section is quite inaccurate #1407
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Comments
I'm going double-check every single snippet you posted and reply with my findings shortly. |
|
@rustbot claim |
# for free
to join this conversation on GitHub.
Already have an account?
# to comment
Uh oh!
There was an error while loading. Please reload this page.
I wrote a lot about this here, but I will recreate the demonstrations in this issue.
Some familiarity with how type bounds work with the default lifetime is important to understand this issue. In particular, references do have the type bound.
Factual corrections
&dyn Trait
default lifetime can be inferred in expressionsThe reference says the default is only inferred in expressions if there are no type bounds and the default outside of expressions is
'static
, but this is not true.Example
This example compiles. Playground.
I believe it is due to this change (accepted without FCP).
Note, however, that if you annotate a named lifetime for the generic type (here the reference lifetime), then the default is that named lifetime. This is the behavior the reference currently says always happens.
Example
This example does not compile. Playground.
The default lifetime for types with multiple bounds can be inferred in expressions
The reference says that for types with multiple bounds, an explicit bound must be specified. However, this is not true in expressions.
Example
This example compiles. Playground.
Trait bounds can override type bounds
According to the reference, trait bounds aren't considered for the default lifetime if the type has bounds. That is, according to the reference, type bounds override trait bounds. However, this is not true: trait bounds can override type bounds.
(When exactly this happens is complicated; see further below.)
Examples
This example compiles. Playground.
This example compiles, but fails with the commented change. Playground.
'static
trait bounds always override type boundsAs a special case of trait bounds overriding type bounds, a
'static
trait bound always applies.Example
This example compiles. Playground.
This is true even if there are multiple lifetime bounds and one of them is
'static
(in contrast with the analogous case for type bounds, which remains ambiguous).A single trait bound is not always the default
According to the reference, if the type has no lifetime bound and the trait has a single lifetime bound, the default trait object lifetime is the bound on the trait. This is true if the bound is
'static
, as covered above. However, it is not always true for a non-'static
bound.(Again, when it is or isn't true -- when trait bounds "apply" or not -- is complicated).
Example
This example compiles. Playground.
The default depends on lifetimes being early or late bound
According to the reference, the default lifetime only depends on the presence or absence of bounds on type and traits. However, the presence or absence of bounds on function lifetime parameters also plays a role. Namely, early-bound lifetime parameters (lifetimes involved in a where clause) act differently than late-bound lifetime parameters.
Example
This example does not compile. Playground. However, the only change from the last is making the lifetime parameter
'a
early-bound.This behavior has been known about for some time. Note that the linked issue was cited when the current reference material was written; I don't know why the information wasn't included in the reference at that time.
The default lifetime can override the wildcard
'_
notationAccording to the reference, using
'_
in place of elision restores "the usual elision rules". However, this is not always true: trait bounds override both type bounds and the wildcard'_
lifetime in function bodies.Examples
This example does not compile. Playground.
This example does not compile. Playground.
This example does not compile. Playground. The only difference from the last example is making the reference lifetime explicit. (As was noted above, this changes the default lifetime when there is no trait bound; this demonstrates that the trait bound still overrides the type bound in this scenario.)
Other underdocumented behavior of note
I don't think the current reference material contradicts these observations, but it doesn't point them out either.
Trait bounds introduce implied bounds on the trait object lifetime
Similar to how
&'a &'b ()
introduces an implied'b: 'a
bound,trait Trait<'b>: 'b
introduces an implied'b: 'a
bound ondyn Trait<'b> + 'a
. (The bound is always present, e.g. even if the trait object lifetime is elided.)Examples
This example compiles. Playground.
This example does not compile. Playground.
Type bounds of aliases take precedence
That's to say, if you have an alias without the lifetime bound, it will act like
Box<T>
(default is often'static
) even if the underlying type had the bound. And if you have an alias with the lifetime bound, it will act like&T
(default is often the bound lifetime) even if the underlying type did not have the bound.Examples
This example compiles. Playground.
This example does not compile. Playground.
See issue #100270.
Bounds on associated types and GATs don't change the default
This is true when the bounds are placed on the associated type or GAT itself.
Example
This example does not compile. Playground.
However, it is also true for GATs with bound type parameters.
Example
This example does not compile. Playground.
This latter concern is tracked in #115379 (which is also where the example is from).
Overview of the actual behavior
Type bounds always apply if there are no trait bounds, but also in other circumstances we'll cover below.
When type bounds apply:
'_
always restores the "normal" elision rules'static
everywhere elseBox<T>
'a
'a
is explicitly annotated in an expression, the default is'a
'a
everywhere else&T
,Ref<'a, T>
'static
)When there are trait bounds, the bounds always implicitly apply to the trait object lifetime, whether that bound is elided, the wildcard
'_
, or explicitly annotated. In particular, if the trait has a'static
bound, the trait object lifetime is effectively always'static
. This is considered unambiguous even if there are other lifetime bounds (in contrast with type bounds).Therefore, when a trait has a
'static
bound, irregardless of anything else'static
(even when technically inferred or another lifetime parameter)From here on, we assume any trait bounds are non-
'static
.When there are trait bounds, they usually apply fully. The exception is function signatures, which we'll cover separately.
When trait bounds are present and apply fully:
'_
usually restores the "normal" elision rules'_
acts like full elision'a
'a
In function signatures when trait bounds are present, trait bounds may apply fully, partially, or not at all (falling back to type bounds). I've written up the detailed behavior in #47078; in summary:
'static
, the default lifetime is'static
dyn Double<'a, 'a>
for traitDouble<'a, b>: 'a + 'b
When trait bounds partially apply, interaction with the inferred bounds from the trait (which are always in effect) can create surprising behavior, as explored in the linked issue.
Other behavior of note:
The text was updated successfully, but these errors were encountered: