Lifetime defaults (e.g. type Ref<'a='static, T> = &'a T
) are difficult for the same reason default-aware inference is difficult. In overly short, we don't currently allow lifetime defaults because lifetimes are always inferred if unmentioned, and we don't currently have the capability to both default and infer the same inference variable.
But we now have a way to explicitly request lifetime inference â '_
. There's an existing lint to suggest using '_
where elided lifetimes are used (elided_lifetimes_in_paths
, but despite being part of the rust_2018_idioms
lint group[1], it's still allow-by-default in all editions, due AIUI in major part to disagreement if it's an improvement for things like &fmt::Formatter
to add <'_>
when it already contains a visibly elided lifetime.
I'd like to propose a middle-ground: given some type Ref<'a, T>
, continue to not lint on Ref
, but lint on Ref<T>
. Specifically, don't lint when the entire generic list is omitted, but lint when the generic list is present but omits any lifetimes. For comparison, consider that a generic list not specifying a slot for a type or const generic is an error, as is specifying any but not all lifetime parameters.
The reason to start linting on implicit lifetime generic elision is that it offers us the potential to change what it means in a future edition. Specifically (as per the title of the thread), I'd like to propose that in a future edition, specifying a generics list which includes a lifetime generic but not providing a lifetime argument uses the default provided lifetime if it exists, and error if there is no default provided (requiring the use of '_
). This brings lifetime generics in line with the other generics' elision/inference behavior. Order mixing would still be disallowed. This does pessimize a custom Ref
type even further from native references, but undoing that deficit is extremely difficult for minimal gain[2].
In existing editions, the default lifetime would be allowed in definitions (likely with an extra warning) but only impact diagnostics at the use site. e.g. suggest either '_
or 'default
in the elision (edition migration) warning and 'default
when inference fails (if 'default
can be named in the scope, meaning the specified default, not a keyword lifetime). It's technically possible to make specifying one but not all lifetimes in current editions use the defaults for the remaining lifetimes instead of error, but I'd suggest leaving it as an error, prioritizing in-edition consistency over matching future-edition behavior; a question to resolve in the stabilization period.
If we decide we want to do this, around now is the time to start considering it to give appropriate lead time on the warning before edition2024 rides the train.
-
It still feels backwards to write
#[warn(rust_2018_idioms)]
... can we rename/alias that lint to something likepre_2018_idioms
orrust_2018_nonidioms
to fix the polarity here? We did a similar rename forbad_style
tononstandard_style
to make#[allow(bad_style)]
less needlessly judgemental. âŠī¸ -
Field projection has a chance, but at least
Deref
andIndex
evaluate to a place, so dereference and index expressions cannot ever produce a custom reference wrapper type. What they could do is produce an associatedDeref
type (like how in C++ the requirement foroperator->
is to return a type with anoperator->
) with the same target type, which would then itself again be dereferenced (recursively until dereferencing&Self::Target
) to compute the place. Temporary lifetime extension rules would then apply for the temporary intermediateimpl Deref
. Where proper reference semantics are desirable and no guardDrop
impl, though,&Cell<T>
should be used instead ofRef<T>
, and we should continue to (slowly) pursue custom pointee metadata types to make this more achievable. (I'd be ecstatic even just for "dyn const
" extra-fat-pointer support as a generalization of array unsizing.) âŠī¸