Summary

Type system changes to address the outlives relation with respect to projections, and to better enforce that all types are well-formed (meaning that they respect their declared

宣言
bounds). The current implementation
実装
can be both unsound (#24622), inconvenient (#23442), and surprising (#21748, #25692). The changes are as follows:
下記の、次に続く、追従する

  • Simplify the outlives relation to be syntactically based.
    基となる、基底(の)
  • Specify
    特定する、指定する、規定する
    improved rules for the outlives relation and projections.
  • Specify
    特定する、指定する、規定する
    more specifically
    特に
    where WF bounds
    制限する、結び付けられて
    are enforced, covering several cases missing from the implementation.
    実装

The proposed changes here have been tested and found to cause only a modest number of regressions (about two dozen root regressions were previously found on crates.io; however, that run did not yet include all the provisions from this RFC; updated numbers coming soon). In order

順序
to minimize the impact on users, the plan is to first introduce
導入する
the changes in two stages:

  1. Initially, warnings will be issued for cases that violate
    違反する
    the rules specified
    特定する、指定する、規定する
    in this RFC. These warnings are not lints and cannot be silenced except by correcting the code such that it type-checks under the new rules.
  2. After one release cycle, those warnings will become errors.

Note that although the changes do cause regressions, they also cause some code (like that in #23442) which currently gets errors to compile successfully.

Motivation

TL;DR

This is a long detailed RFC that is attempting to specify

特定する、指定する、規定する
in some detail aspects of the type system that were underspecified or buggily implemented
実装する
before. This section
just summarizes the effect on existing Rust code in terms
項、用語
of changes that may be required.

Warnings first, errors later. Although the changes described

記述する
in this RFC are necessary for soundness (and many of them are straight-up bugfixes), there is some impact on existing code. Therefore the plan is to first issue warnings for a release cycle and then transition to hard errors, so as to ease the migration.

Associated type projections and lifetimes work more smoothly. The current rules for relating associated type projections (like T::Foo) and lifetimes are somewhat cumbersome. The newer rules are more flexible, so that e.g. we can deduce that T::Foo: 'a if T: 'a, and similarly

同様に
that T::Foo is well-formed if T is well-formed. As a bonus, the new rules are also sound. ;)

Simpler outlives relation. The older definition

定義
for the outlives relation T: 'a was rather subtle. The new rule basically says that if all type/lifetime parameters
仮引数
appearing
現れる
in the type T must outlive 'a, then T: 'a (though there can also be other ways for us to decide that T: 'a is valid,
有効な、正しい
such as in-scope where clauses). So for example fn(&'x X): 'a if 'x: 'a and X: 'a (presuming that X is a type parameter). The older rules were based
基となる、基底(の)
on what kind of data was actually reachable
到達可能
, and hence accepted
受け付ける、受理する
this type (since no data of &'x X is reachable
到達可能
from a function pointer). This change primarily affects struct
構造、構造体
declarations,
宣言
since they may now require additional
追加の
outlives bounds:
制限する、結び付けられて

#![allow(unused)] fn main() { // OK now, but after this RFC requires `X: 'a`: struct Foo<'a, X> { f: fn(&'a X) // (because of this field) } }

More types are sanity checked. Generally Rust requires that if you have a type like SomeStruct<T>, then whatever where clauses

句、節
are declared
宣言
on SomeStruct must hold
保有する、有効である
for T (this is called
呼び出し
being "well-formed"). For example, if SomeStruct is declared
宣言
like so:

#![allow(unused)] fn main() { struct SomeStruct<T:Eq> { .. } }

then this implies

暗黙の
that SomeStruct<f32> is ill-formed, since f32 does not implement
実装する
Eq (just PartialEq). However, the current compiler doesn't check this in associated type definitions:
定義

#![allow(unused)] fn main() { impl Iterator for SomethingElse { type Item = SomeStruct<f32>; // accepted now, not after this RFC } }

Similarly,

同様に
WF checking was skipped for trait object types and fn arguments.
引数
This means that fn(SomeStruct<f32>) would be considered
みなす、考慮する
well-formed today, though attempting to call
呼び出し
the function would be an error. Under this RFC, that fn type is not well-formed (though sometimes when there are higher-ranked regions, WF checking may still be deferred until the point where the fn is called).

There are a few other places where similar

似ている、同様の
requirements were being overlooked before but will now be enforced. For example, a number of traits like the following
下記の、次に続く、追従する
were found in the wild:

#![allow(unused)] fn main() { trait Foo { // currently accepted, but should require that Self: Sized fn method(&self, value: Option<Self>); } }

To be well-formed, an Option<T> type requires that T: Sized. In this case, though T=Self, and Self is not Sized by default. Therefore, this trait should be declared

宣言
trait Foo: Sized to be legal.
(文法的に)適格
The compiler is currently attempting to enforce these rules, but many cases were overlooked in practice.

Impact on crates.io

This RFC has been largely implemented

実装する
and tested against crates.io. A total of 43 (root) crates are affected by the changes. Interestingly, the vast majority of warnings/errors that occur
起こる
are not due to new rules introduced by this RFC
, but rather due to older rules being more correctly enforced.

Of the affected crates, 40 are receiving future compatibility

互換性
warnings and hence continue to build for the time being. In the remaining three cases, it was not possible to isolate the effects of the new rules, and hence the compiler reports an error rather than a future compatibility
互換性
warning.

What follows

下記の、次に続く、追従する
is a breakdown of the reason that crates on crates.io are receiving errors or warnings. Each row in the table corresponds
照応する
to one of the explanations above.

ProblemFuture-compat. warningsErrors
More types are sanity checked353
Simpler outlives relation5

As you can see, by far the largest source

元の
of problems is simply that we are now sanity checking more types. This was always the intent, but there were bugs in the compiler that led to it either skipping checking altogether or only partially applying
適用する
the rules. It is interesting to drill down a bit further
さらなる、それ以上
into the 38 warnings/errors that resulted
結果、戻り値
from more types being sanity checked in order
順序
to see what kinds of mistakes are being caught:

CaseProblemNumber
1Self: Sized required26
2Foo: Bar required11
3Not object safe1

An example of each case follows:

下記の、次に続く、追従する

Cases 1 and 2. In the compiler today, types appearing

現れる
in trait methods are incompletely checked. This leads to a lot of traits with insufficient bounds.
制限する、結び付けられて
By far the most common example was that the Self parameter
仮引数
would appear
現れる
in a context
文脈、背景
where it must be sized, usually when it is embedded
埋め込む
within another type (e.g., Option<Self>). Here is an example:

#![allow(unused)] fn main() { trait Test { fn test(&self) -> Option<Self>; // ~~~~~~~~~~~~ // Incorrectly permitted before. } }

Because Option<T> requires that T: Sized, this trait should be declared

宣言
as follows:
下記の、次に続く、追従する

#![allow(unused)] fn main() { trait Test: Sized { fn test(&self) -> Option<Self>; } }

Case 2. Case 2 is the same as case 1, except that the missing bound

制限する、結び付けられて
is some trait other than Sized, or in some cases an outlives bound
制限する、結び付けられて
like T: 'a.

Case 3. The compiler currently permits

許す
non-object-safe traits to be used as types, even if objects could never actually be created (#21953).

Projections and the outlives relation

RFC 192 introduced the outlives relation T: 'a and described

記述する
the rules that are used to decide when one type outlives a lifetime. In particular, the RFC describes
記述する
rules that govern how the compiler determines
決定する
what kind of borrowed data may be "hidden" by a generic type. For example, given
与えられた
this function signature:
シグネチャ

#![allow(unused)] fn main() { fn foo<'a,I>(x: &'a I) where I: Iterator { ... } }

the compiler is able to use implied

暗黙の
region bounds
制限する、結び付けられて
(described more below) to automatically
自動的に
determine
決定する
that:

  • all borrowed content in the type I outlives the function body;
  • all borrowed content in the type I outlives the lifetime 'a.

When associated types were introduced in RFC 195, some new rules were required to decide when an "outlives relation" involving a projection (e.g., I::Item: 'a) should hold.

保有する、有効である
The initial
初期
rules were very conservative. This led to the rules from RFC 192 being adapted to cover associated type projections like I::Item. Unfortunately, these adapted rules are not ideal, and can still lead to annoying errors in some situations. Finding a better solution has been on the agenda for some time.

Simultaneously, we realized in #24622 that the compiler had a bug that caused it to erroneously

誤って
assume
仮定する
that every projection like I::Item outlived the current function body, just as it assumes that type parameters
仮引数
like I outlive the current function body. This bug can lead to unsound behavior.
ふるまい
Unfortunately, simply implementing
実装する
the naive fix for #24622 exacerbates the shortcomings of the current rules for projections, causing widespread compilation failures in all sorts of reasonable and obviously correct code.

This RFC describes

記述する
modifications to the type system that both restore soundness and make working with associated types more convenient in some situations. The changes are largely but not completely backwards compatible.

Well-formed types

A type is considered

みなす、考慮する
well-formed (WF) if it meets some simple correctness criteria. For builtin types like &'a T or [T], these criteria are built into the language.
言語
For user-defined types like a struct
構造、構造体
or an enum, the criteria are declared
宣言
in the form
形式、形態、形作る
of where clauses.
句、節
In general,
一般
all types that appear
現れる
in the source
元の
and elsewhere should be well-formed.

For example, consider

考える、みなす
this type, which combines
合体する、組み合わせる
a reference
参照
to a hashmap and a vector of additional
追加の
key/value pairs:

#![allow(unused)] fn main() { struct DeltaMap<'a, K, V> where K: Hash + 'a, V: 'a { base_map: &'a mut HashMap<K,V>, additional_values: Vec<(K,V)> } }

Here, the WF criteria for DeltaMap<K,V> are as follows:

下記の、次に続く、追従する

  • K: Hash, because of the where-clause,
  • K: 'a, because of the where-clause,
  • V: 'a, because of the where-clause
  • K: Sized, because of the implicit
    暗黙の
    Sized bound
    制限する、結び付けられて
  • V: Sized, because of the implicit
    暗黙の
    Sized bound
    制限する、結び付けられて

Let's look at those K:'a bounds

制限する、結び付けられて
a bit more closely. If you leave them out, you will find that the the structure definition
定義
above does not type-check. This is due to the requirement that the types of all fields in a structure definition
定義
must be well-formed. In this case, the field base_map has the type &'a mut HashMap<K,V>, and this type is only valid
有効な、正しい
if K: 'a and V: 'a hold.
保有する、有効である
Since we don't know what K and V are, we have to surface this requirement in the form
形式、形態、形作る
of a where-clause, so that users of the struct
構造、構造体
know that they must maintain this relationship in order
順序
for the struct
構造、構造体
to be interally coherent.

An aside: explicit
明示的な
WF requirements on types

You might wonder why you have to write K:Hash and K:'a explicitly.

明示的に
After all, they are obvious from the types of the fields. The reason is that we want to make it possible to check whether a type like DeltaMap<'foo,T,U> is well-formed without having to inspect the types of the fields -- that is, in the current design,
設計(する)
the only information that we need to use to decide if DeltaMap<'foo,T,U> is well-formed is the set
セットする、集合
of bounds
制限する、結び付けられて
and where-clauses.

This has real consequences

結果
on usability. It would be possible for the compiler to infer bounds
制限する、結び付けられて
like K:Hash or K:'a, but the origin of the bound
制限する、結び付けられて
might be quite remote. For example, we might have a series of types like:

#![allow(unused)] fn main() { struct Wrap1<'a,K>(Wrap2<'a,K>); struct Wrap2<'a,K>(Wrap3<'a,K>); struct Wrap3<'a,K>(DeltaMap<'a,K,K>); }

Now, for Wrap1<'foo,T> to be well-formed, T:'foo and T:Hash must hold,

保有する、有効である
but this is not obvious from the declaration
宣言
of Wrap1. Instead, you must trace deeply through its fields to find out that this obligation exists.

Implied
暗黙の
lifetime bounds
制限する、結び付けられて

To help avoid

避ける、回避する
undue annotation, Rust relies on implied
暗黙の
lifetime bounds
制限する、結び付けられて
in certain contexts. Currently, this is limited to fn bodies. The idea is that for functions, we can make callers
呼び出し側
do some portion of the WF validation, and let the callees just assume
仮定する
it has been done already. (This is in contrast
対比する、対比
to the type definition,
定義
where we required that the struct
構造、構造体
itself declares
宣言
all of its requirements up front in the form
形式、形態、形作る
of where-clauses.)

To see this in action, consider

考える、みなす
a function that uses a DeltaMap:

#![allow(unused)] fn main() { fn foo<'a,K:Hash,V>(d: DeltaMap<'a,K,V>) { ... } }

You'll notice that there are no K:'a or V:'a annotations required here. This is due to implied

暗黙の
lifetime bounds
制限する、結び付けられて
. Unlike structs,
構造、構造体
a function's caller
呼び出し側
must examine not only the explicit
明示的な
bounds
制限する、結び付けられて
and where-clauses, but also the argument
引数
and return types. When there are generic type/lifetime parameters
仮引数
involved, the caller
呼び出し側
is in charge of ensuring that those types are well-formed. (This is in contrast
対比する、対比
with type definitions,
定義
where the type is in charge of figuring out its own requirements and listing
リスト、列挙する
them in one place.)

As the name "implied

暗黙の
lifetime bounds"
制限する、結び付けられて
suggests, we currently limit implied
暗黙の
bounds
制限する、結び付けられて
to region relationships. That is, we will implicitly
暗黙的に
derive
派生する
a bound
制限する、結び付けられて
like K:'a or V:'a, but not K:Hash -- this must still be written manually. It might be a good idea to change this, but that would be the topic of a separate
分割する
RFC.

Currently, implied

暗黙の
bound
制限する、結び付けられて
are limited to fn bodies. This RFC expands the use of implied
暗黙の
bounds
制限する、結び付けられて
to cover impl definitions
定義
as well, since otherwise
さもなければ
the annotation burden is quite painful. More on this in the next section.

NB. There is an additional

追加の
problem concerning the interaction of implied
暗黙の
bounds
制限する、結び付けられて
and contravariance (#25860). To better separate
分割する
the issues, this will be addressed in a follow-up RFC that should appear
現れる
shortly.

Missing WF checks

Unfortunately, the compiler currently fails to enforce WF in several important cases. For example, the following

program is accepted:
受け付ける、受理する

#![allow(unused)] fn main() { struct MyType<T:Copy> { t: T } trait ExampleTrait { type Output; } struct ExampleType; impl ExampleTrait for ExampleType { type Output = MyType<Box<i32>>; // ~~~~~~~~~~~~~~~~ // | // Note that `Box<i32>` is not `Copy`! } }

However, if we simply naively add the requirement that associated types must be well-formed, this results

結果、戻り値
in a large annotation burden (see e.g. PR 25701). For example, in practice, many iterator implementation
実装
break due to region relationships:

#![allow(unused)] fn main() { impl<'a, T> IntoIterator for &'a LinkedList<T> { type Item = &'a T; ... } }

The problem here is that for &'a T to be well-formed, T: 'a must hold,

保有する、有効である
but that is not specified
特定する、指定する、規定する
in the where clauses.
句、節
This RFC proposes using implied
暗黙の
bounds
制限する、結び付けられて
to address this concern -- specifically,
特に
every impl is permitted
許す
to assume
仮定する
that all types which appear
現れる
in the impl header (trait reference) are well-formed, and in turn each "user" of an impl will validate this requirement whenever they project out of a trait reference
参照
(e.g., to do a method call,
呼び出し
or normalize an associated type).

Detailed design
設計(する)

This section

dives into detail on the proposed type rules.

A little type grammar
文法

We extend

拡張する
the type grammar
文法
from RFC 192 with projections and slice types:

T = scalar (i32, u32, ...) // Boring stuff | X // Type variable | Id<P0..Pn> // Nominal type (struct, enum) | &r T // Reference (mut doesn't matter here) | O0..On+r // Object type | [T] // Slice type | for<r..> fn(T1..Tn) -> T0 // Function pointer | <P0 as Trait<P1..Pn>>::Id // Projection P = r // Region name | T // Type O = for<r..> TraitId<P1..Pn> // Object type fragment r = 'x // Region name

We'll use this to describe the rules in detail.

A quick note on terminology: an "object type fragment" is part of an object type: so if you have Box<FnMut()+Send>, FnMut() and Send are object type fragments. Object type fragments are identical

同一の(である)
to full trait references,
参照
except that they do not have a self type (no P0).

Syntactic definition
定義
of the outlives relation

The outlives relation is defined

定義する
in purely syntactic terms
項、用語
as follows.
下記の、次に続く、追従する
These are inference rules written in a primitive ASCII notation.
記法
:) As part of defining
定義する
the outlives relation, we need to track the set
セットする、集合
of lifetimes that are bound
制限する、結び付けられて
within the type we are looking at. Let's call
呼び出し
that set
セットする、集合
R=<r0..rn>. Initially, this set
セットする、集合
R is empty,
空の
but it will grow as we traverse
走査する
through types like fns or object fragments, which can bind
束ねる/くっつける
region names via for<..>.

Simple outlives rules

Here are the rules covering the simple cases, where no type parameters

仮引数
or projections are involved:

OutlivesScalar: -------------------------------------------------- R ⊢ scalar: 'a OutlivesNominalType: ∀i. R ⊢ Pi: 'a -------------------------------------------------- R ⊢ Id<P0..Pn>: 'a OutlivesReference: R ⊢ 'x: 'a R ⊢ T: 'a -------------------------------------------------- R ⊢ &'x T: 'a OutlivesObject: ∀i. R ⊢ Oi: 'a R ⊢ 'x: 'a -------------------------------------------------- R ⊢ O0..On+'x: 'a OutlivesFunction: ∀i. R,r.. ⊢ Ti: 'a -------------------------------------------------- R ⊢ for<r..> fn(T1..Tn) -> T0: 'a OutlivesFragment: ∀i. R,r.. ⊢ Pi: 'a -------------------------------------------------- R ⊢ for<r..> TraitId<P0..Pn>: 'a

Outlives for lifetimes

The outlives relation for lifetimes depends on whether the lifetime in question was bound

制限する、結び付けられて
within a type or not. In the usual case, we decide the relationship between two lifetimes by consulting the environment,
環境
or using the reflexive property.
特徴、性質
Lifetimes representing
表現する
scopes within the current fn have a relationship derived from the code itself, while lifetime parameters
仮引数
have relationships defined
定義する
by where-clauses and implied
暗黙の
bounds.
制限する、結び付けられて

OutlivesRegionEnv: 'x ∉ R // not a bound region ('x: 'a) in Env // derivable from where-clauses etc -------------------------------------------------- R ⊢ 'x: 'a OutlivesRegionReflexive: -------------------------------------------------- R ⊢ 'a: 'a OutlivesRegionTransitive: R ⊢ 'a: 'c R ⊢ 'c: 'b -------------------------------------------------- R ⊢ 'a: 'b

For higher-ranked lifetimes, we simply ignore

無視する
the relation, since the lifetime is not yet known. This means for example that for<'a> fn(&'a i32): 'x holds,
保有する、有効である
even though we do not yet know what region 'a is (and in fact it may be instantiated many times with different values on each call
呼び出し
to the fn).

OutlivesRegionBound: 'x ∈ R // bound region -------------------------------------------------- R ⊢ 'x: 'a

Outlives for type parameters
仮引数

For type parameters,

仮引数
the only way to draw "outlives" conclusions is to find information in the environment
環境
(which is being threaded implicitly
暗黙的に
here, since it is never modified). In terms
項、用語
of a Rust program, this means both explicit
明示的な
where-clauses and implied
暗黙の
bounds
制限する、結び付けられて
derived from the signature
シグネチャ
(discussed below).

OutlivesTypeParameterEnv: X: 'a in Env -------------------------------------------------- R ⊢ X: 'a

Outlives for projections

Projections have the most possibilities. First, we may find information in the in-scope where clauses,

句、節
as with type parameters,
仮引数
but we can also consult the trait definition
定義
to find bounds
制限する、結び付けられて
(consider an associated type declared
宣言
like type Foo: 'static
静的な
). These rule only apply
適用する
if there are no higher-ranked lifetimes in the projection; for simplicity's sake, we encode
符号化する
that by requiring an empty
空の
list
リスト、列挙する
of higher-ranked lifetimes. (This is somewhat stricter than necessary, but reflects the behavior
ふるまい
of my prototype implementation.)

OutlivesProjectionEnv: <P0 as Trait<P1..Pn>>::Id: 'b in Env <> ⊢ 'b: 'a -------------------------------------------------- <> ⊢ <P0 as Trait<P1..Pn>>::Id: 'a OutlivesProjectionTraitDef: WC = [Xi => Pi] WhereClauses(Trait) <P0 as Trait<P1..Pn>>::Id: 'b in WC <> ⊢ 'b: 'a -------------------------------------------------- <> ⊢ <P0 as Trait<P1..Pn>>::Id: 'a

All the rules covered so far already exist today. This last rule, however, is not only new, it is the crucial insight of this RFC. It states that if all the components

構成要素
in a projection's trait reference
参照
outlive 'a, then the projection must outlive 'a:

OutlivesProjectionComponents: ∀i. R ⊢ Pi: 'a -------------------------------------------------- R ⊢ <P0 as Trait<P1..Pn>>::Id: 'a

Given

与えられた
the importance of this rule, it's worth spending a bit of time discussing it in more detail. The following
下記の、次に続く、追従する
explanation is fairly informal. A more detailed look can be found in the appendix.

Let's begin with a concrete

具体的な/具象的な
example of an iterator type, like std::vec::Iter<'a,T>. We are interested in the projection of Iterator::Item:

<Iter<'a,T> as Iterator>::Item

or, in the more succint (but potentially ambiguous) form:

形式、形態、形作る

Iter<'a,T>::Item

Since I'm going to be talking a lot about this type, let's just call

呼び出し
it <PROJ> for now. We would like to determine
決定する
whether <PROJ>: 'x holds.
保有する、有効である

Now, the easy way to solve <PROJ>: 'x would be to normalize <PROJ> by looking at the relevant impl:

#![allow(unused)] fn main() { impl<'b,U> Iterator for Iter<'b,U> { type Item = &'b U; ... } }

From this impl, we can conclude that <PROJ> == &'a T, and thus

それゆえに、従って、
reduce <PROJ>: 'x to &'a T: 'x, which in turn holds
保有する、有効である
if 'a: 'x and T: 'x (from the rule OutlivesReference).

But often we are in a situation where we can't normalize the projection (for example, a projection like I::Item where we only know that I: Iterator). What can we do then? The rule OutlivesProjectionComponents says that if we can conclude that every lifetime/type parameter

仮引数
Pi to the trait reference
参照
outlives 'x, then we know that a projection from those parameters
仮引数
outlives 'x. In our example, the trait reference
参照
is <Iter<'a,T> as Iterator>, so that means that if the type Iter<'a,T> outlives 'x, then the projection <PROJ> outlives 'x. Now, you can see that this trivially reduces to the same result
結果、戻り値
as the normalization, since Iter<'a,T>: 'x holds
保有する、有効である
if 'a: 'x and T: 'x (from the rule OutlivesNominalType).

OK, so we've seen that applying

適用する
the rule OutlivesProjectionComponents comes up with the same result
結果、戻り値
as normalizing (at least in this case), and that's a good sign.
符号
But what is the basis of the rule?

The basis of the rule comes from reasoning about the impl that we used to do normalization. Let's consider

考える、みなす
that impl again, but this time hide the actual
実際の
type that was specified:
特定する、指定する、規定する

#![allow(unused)] fn main() { impl<'b,U> Iterator for Iter<'b,U> { type Item = /* <TYPE> */; ... } }

So when we normalize <PROJ>, we obtain

得る
the result
結果、戻り値
by applying
適用する
some substitution
置き換え
Θ to <TYPE>. This substitution
置き換え
is a mapping from the lifetime/type parameters
仮引数
on the impl to some specific
特定の
values, such that <PROJ> == Θ <Iter<'b,U> as Iterator>::Item. In this case, that means Θ would be ['b => 'a, U => T] (and of course <TYPE> would be &'b U, but we're not supposed to rely on that).

The key idea for the OutlivesProjectionComponents is that the only way that <TYPE> can fail to outlive 'x is if either:

  • it names some lifetime parameter
    仮引数
    'p where 'p: 'x does not hold;
    保有する、有効である
    or,
  • it names some type parameter
    仮引数
    X where X: 'x does not hold.
    保有する、有効である

Now, the only way that <TYPE> can refer

参照する
to a parameter
仮引数
P is if it is brought in by the substitution
置き換え
Θ. So, if we can just show that all the types/lifetimes that in the range
範囲
of Θ outlive 'x, then we know that Θ <TYPE> outlives 'x.

Put yet another way: imagine that you have an impl with no parameters

仮引数
, like:

#![allow(unused)] fn main() { impl Iterator for Foo { type Item = /* <TYPE> */; } }

Clearly, whatever <TYPE> is, it can only refer

参照する
to the lifetime 'static
静的な
. So <Foo as Iterator>::Item: 'static
静的な
holds.
保有する、有効である
We know this is true without ever knowing what <TYPE> is -- we just need to see that the trait reference
参照
<Foo as Iterator> doesn't have any lifetimes or type parameters
仮引数
in it, and hence the impl cannot refer
参照する
to any lifetime or type parameters.
仮引数

Implementation
実装
complications

The current region inference code only permits

許す
constraints
制約
of the form:
形式、形態、形作る

C = r0: r1 | C AND C

This is convenient because a simple fixed-point iteration

反復、繰り返し
suffices to find the minimal regions which satisfy
満たす、満足させる
the constraints.
制約

Unfortunately, this constraint

制約
model does not scale
規模を変更する
to the outlives rules for projections. Consider
考える、みなす
a trait reference
参照
like <T as Trait<'X>>::Item: 'Y, where 'X and 'Y are both region variables whose value is being inferred. At this point, there are several inference rules which could potentially apply.
適用する
Let us assume
仮定する
that there is a where-clause in the environment
環境
like <T as Trait<'a>>::Item: 'b. In that case, if 'X == 'a and 'b: 'Y, then we could employ the OutlivesProjectionEnv rule. This would correspond
照応する
to a constraint
制約
set
セットする、集合
like:

C = 'X:'a AND 'a:'X AND 'b:'Y

Otherwise,

さもなければ
if T: 'a and 'X: 'Y, then we could use the OutlivesProjectionComponents rule, which would require a constraint
制約
set
セットする、集合
like:

C = C1 AND 'X:'Y

where C1 is the constraint

制約
set
セットする、集合
for T:'a.

As you can see, these two rules yielded

産出する、出力する
distinct
区別された/独立した
constraint
制約
sets.
セットする、集合
Ideally, we would combine them with an OR constraint,
制約
but no such constraint
制約
is available. Adding
たす
such a constraint
制約
complicates how inference works, since a fixed-point iteration
反復、繰り返し
is no longer sufficient.

This complication is unfortunate, but to a large extent already exists with where-clauses and trait matching (see e.g. #21974). (Moreover, it seems to be inherent to the concept of assocated types, since they take

とる
several inputs (the parameters
仮引数
to the trait) which may or may not be related to the actual
実際の
type definition
定義
in question.)

For the time being, the current implementation

実装
takes
とる
a pragmatic approach based
基となる、基底(の)
on heuristics. It first examines whether any region bounds
制限する、結び付けられて
are declared
宣言
in the trait and, if so, prefers to use those. Otherwise,
さもなければ
if there are region variables in the projection, then it falls back to the OutlivesProjectionComponents rule. This is always sufficient but may be stricter than necessary. If there are no region variables in the projection, then it can simply run inference to completion and check each of the other two rules in turn. (It is still necessary to run inference because the bound
制限する、結び付けられて
may be a region variable.) So far this approach has sufficed for all situations encountered
出会う
in practice. Eventually, we should extend
拡張する
the region inferencer to a richer model that includes "OR" constraints.
制約

The WF relation

This section

describes
記述する
the "well-formed" relation. In previous
前の
RFCs
, this was combined
合体する、組み合わせる
with the outlives relation. We separate
分割する
it here for reasons that shall become clear when we discuss WF conditions
条件
on impls.

The WF relation is really pretty simple: it just says that a type is "self-consistent". Typically,

一般的に、典型的に
this would include validating
有効な、正しい
scoping (i.e., that you don't refer
参照する
to a type parameter
仮引数
X if you didn't declare
宣言
one), but we'll take
とる
those basic conditions
条件
for granted.

WfScalar: -------------------------------------------------- R ⊢ scalar WF WfParameter: -------------------------------------------------- R ⊢ X WF // where X is a type parameter WfTuple: ∀i. R ⊢ Ti WF ∀i<n. R ⊢ Ti: Sized // the *last* field may be unsized -------------------------------------------------- R ⊢ (T0..Tn) WF WfNominalType: ∀i. R ⊢ Pi Wf // parameters must be WF, C = WhereClauses(Id) // and the conditions declared on Id must hold... R ⊢ [P0..Pn] C // ...after substituting parameters, of course -------------------------------------------------- R ⊢ Id<P0..Pn> WF WfReference: R ⊢ T WF // T must be WF R ⊢ T: 'x // T must outlive 'x -------------------------------------------------- R ⊢ &'x T WF WfSlice: R ⊢ T WF R ⊢ T: Sized -------------------------------------------------- [T] WF WfProjection: ∀i. R ⊢ Pi WF // all components well-formed R ⊢ <P0: Trait<P1..Pn>> // the projection itself is valid -------------------------------------------------- R ⊢ <P0 as Trait<P1..Pn>>::Id WF

WF checking and higher-ranked types

There are two places in Rust where types can introduce

導入する
lifetime names into scope: fns and trait objects. These have somewhat different rules than the rest, simply because they modify the set
セットする、集合
R of bound
制限する、結び付けられて
lifetime names. Let's start with the rule for fn types:

WfFn: ∀i. R, r.. ⊢ Ti WF -------------------------------------------------- R ⊢ for<r..> fn(T1..Tn) -> T0 WF

Basically, this rule adds the bound

制限する、結び付けられて
lifetimes to the set
セットする、集合
R and then checks whether the argument
引数
and return type are well-formed. We'll see in the next section
that means that any requirements on those types which reference
参照
bound
制限する、結び付けられて
identifiers
識別子
are just assumed to hold,
保有する、有効である
but the remainder
余り
are checked. For example, if we have a type HashSet<K> which requires that K: Hash, then fn(HashSet<NoHash>) would be illegal
文法違反
since NoHash: Hash does not hold,
保有する、有効である
but for<'a> fn(HashSet<&'a NoHash>) would be legal,
(文法的に)適格
since &'a NoHash: Hash involves a bound
制限する、結び付けられて
region 'a. See the "Checking Conditions"
条件
section
for details.

Note that fn types do not require that T0..Tn be Sized. This is intentional. The limitation that only sized values can be passed as argument

引数
(or returned) is enforced at the time when a fn is actually called,
呼び出し
as well as in actual
実際の
fn definitions,
定義
but is not considered
みなす、考慮する
fundamental to fn types themselves. There are several reasons for this. For one thing, it's forwards compatible with passing DST by value. For another, it means that non-defaulted trait methods to do not have to show that their argument
引数
types are Sized (this will be checked in the implementations,
実装
where more types are known). Since the implicit
暗黙の
Self type parameter
仮引数
is not Sized by default (RFC 546), requiring that argument
引数
types be Sized in trait definitions
定義
proves to be an annoying annotation burden.

The object type rule is similar,

似ている、同様の
though it includes an extra clause:
句、節

WfObject: rᵢ = union of implied region bounds from Oi ∀i. rᵢ: r ∀i. R ⊢ Oi WF -------------------------------------------------- R ⊢ O0..On+r WF

The first two clauses

句、節
here state that the explicit
明示的な
lifetime bound
制限する、結び付けられて
r must be an approximation for the the implicit
暗黙の
bounds
制限する、結び付けられて
rᵢ derived from the trait definitions.
定義
That is, if you have a trait definition
定義
like

#![allow(unused)] fn main() { trait Foo: 'static { ... } }

and a trait object like Foo+'x, when we require that 'static:

静的な
'x (which is true, clearly, but in some cases the implicit
暗黙の
bounds
制限する、結び付けられて
from traits are not 'static
静的な
but rather some named lifetime).

The next clause

句、節
states that all object type fragments must be WF. An object type fragment is WF if its components
構成要素
are WF:

WfObjectFragment: ∀i. R, r.. ⊢ Pi TraitId is object safe -------------------------------------------------- R ⊢ for<r..> TraitId<P1..Pn>

Note that we don't check the where clauses

句、節
declared
宣言
on the trait itself. These are checked when the object is created. The reason not to check them here is because the Self type is not known (this is an object, after all), and hence we can't check them in general.
一般
(But see unresolved questions.)

WF checking a trait reference
参照

In some contexts, we want to check a trait reference,

参照
such as the ones that appear
現れる
in where clauses
句、節
or type parameter
仮引数
bounds.
制限する、結び付けられて
The rules for this are given
与えられた
here:

WfTraitReference: ∀i. R, r.. ⊢ Pi C = WhereClauses(Id) // and the conditions declared on Id must hold... R, r0...rn ⊢ [P0..Pn] C // ...after substituting parameters, of course -------------------------------------------------- R ⊢ for<r..> P0: TraitId<P1..Pn>

The rules are fairly straightforward. The components

構成要素
must be well formed,
形式、形態、形作る
and any where-clauses declared
宣言
on the trait itself much hold.
保有する、有効である

Checking conditions
条件

In various

さまざまな
rules above, we have rules that declare
宣言
that a where-clause must hold,
保有する、有効である
which have the form
形式、形態、形作る
R ̣⊢ WhereClause. Here, R represents
表現する
the set
セットする、集合
of bound
制限する、結び付けられて
regions. It may well be that WhereClause does not use any of the regions in R. In that case, we can ignore
無視する
the bound-regions and simple check that WhereClause holds.
保有する、有効である
But if WhereClause does refer
参照する
to regions in R, then we simply consider
考える、みなす
R WhereClause to hold.
保有する、有効である
Those conditions
条件
will be checked later when the bound
制限する、結び付けられて
lifetimes are instantiated (either through a call
呼び出し
or a projection).

In practical terms,

項、用語
this means that if I have a type like:

#![allow(unused)] fn main() { struct Iterator<'a, T:'a> { ... } }

and a function type like for<'a> fn(i: Iterator<'a, T>) then this type is considered

みなす、考慮する
well-formed without having to show that T: 'a holds.
保有する、有効である
In terms
項、用語
of the rules, this is because we would wind up with a constraint
制約
like 'a T: 'a.

However, if I have a type like

#![allow(unused)] fn main() { struct Foo<'a, T:Eq> { .. } }

and a function type like for<'a> fn(f: Foo<'a, T>), I still must show that T: Eq holds

保有する、有効である
for that function to be well-formed. This is because the condition
条件
which is geneated will be 'a T: Eq, but 'a is not referenced there.

Implied
暗黙の
bounds
制限する、結び付けられて

Implied

暗黙の
bounds
制限する、結び付けられて
can be derived from the WF and outlives relations. The implied
暗黙の
bounds
制限する、結び付けられて
from a type T are given
与えられた
by expanding the requirements that T: WF. Since we currently limit ourselves to implied
暗黙の
region bounds,
制限する、結び付けられて
we we are interesting in extracting
抽出する
requirements of the form:
形式、形態、形作る

  • 'a:'r, where two regions must be related;
  • X:'r, where a type parameter
    仮引数
    X outlives a region; or,
  • <T as Trait<..>>::Id: 'r, where a projection outlives a region.

Some caution is required around projections when deriving implied

暗黙の
bounds.
制限する、結び付けられて
If we encounter
出会う
a requirement that e.g. X::Id: 'r, we cannot for example deduce that X: 'r must hold.
保有する、有効である
This is because while X: 'r is sufficient for X::Id: 'r to hold,
保有する、有効である
it is not necessary for X::Id: 'r to hold.
保有する、有効である
So we can only conclude that X::Id: 'r holds,
保有する、有効である
and not X: 'r.

When should we check the WF relation and under what conditions?

Currently the compiler performs WF checking in a somewhat haphazard way: in some cases (such as impls), it omits

省略する
checking WF, but in others (such as fn bodies), it checks WF when it should not have to. Partly that is due to the fact that the compiler currently connects the WF and outlives relationship into one thing, rather than separating
分割する
them as described
記述する
here.

Constants/statics. The type of a constant

定数
or static
静的な
can be checked for WF in an empty
空の
environment.
環境

Struct/enum declarations.

宣言
In a struct/enum declaration,
宣言
we should check that all field types are WF, given
与えられた
the bounds
制限する、結び付けられて
and where-clauses from the struct
構造、構造体
declaration.
宣言
Also check that where-clauses are well-formed.

Function items. For function items, the environment

環境
consists
構成される
of all the where-clauses from the fn, as well as implied
暗黙の
bounds
制限する、結び付けられて
derived from the fn's argument
引数
types. These are then used to check that the following
下記の、次に続く、追従する
are well-formed:

  • argument
    引数
    types;
  • return type;
  • where clauses;
    句、節
  • types of local variables.

These WF requirements are imposed at each fn or associated fn definition

定義
(as well as within trait items).

Trait impls. In a trait impl, we assume

仮定する
that all types appearing
現れる
in the impl header are well-formed. This means that the initial
初期
environment
環境
for an impl consists
構成される
of the impl where-clauses and implied
暗黙の
bounds
制限する、結び付けられて
derived from its header. Example: Given
与えられた
an impl like impl<'a,T> SomeTrait for &'a T, the environment
環境
would be T: Sized (explicit where-clause) and T: 'a (implied bound
制限する、結び付けられて
derived from &'a T). This environment
環境
is used as the starting point for checking the items:

  • Where-clauses declared
    宣言
    on the trait must be WF.
  • Associated types must be WF in the trait environment.
    環境
  • The types of associated constants
    定数
    must be WF in the trait environment.
    環境
  • Associated fns are checked just like regular
    普通の、正規の
    function items, but with the additional
    追加の
    implied
    暗黙の
    bounds
    制限する、結び付けられて
    from the impl signature.
    シグネチャ

Inherent impls. In an inherent impl, we can assume

仮定する
that the self type is well-formed, but otherwise
さもなければ
check the methods as if they were normal functions. We must check that all items are well-formed, along with the where clauses
句、節
declared
宣言
on the impl.

Trait declarations.

宣言
Trait declarations
宣言
(and defaults) are checked in the same fashion as impls, except that there are no implied
暗黙の
bounds
制限する、結び付けられて
from the impl header. We must check that all items are well-formed, along with the where clauses
句、節
declared
宣言
on the trait.

Type aliases.

別名
Type aliases
別名
are currently not checked for WF, since they are considered
みなす、考慮する
transparent to type-checking. It's not clear that this is the best policy, but it seems harmless, since the WF rules will still be applied
適用する
to the expanded version. See the Unresolved Questions for some discussion on the alternatives
代わりのもの、選択肢
here.

Several points in the list

リスト、列挙する
above made use of implied
暗黙の
bounds
制限する、結び付けられて
based
基となる、基底(の)
on assuming that various
さまざまな
types were WF. We have to ensure
保証する
that those bounds
制限する、結び付けられて
are checked on the reciprocal side, as follows:
下記の、次に続く、追従する

Fns being called.

呼び出し
Before calling
呼び出し
a fn, we check that its argument
引数
and return types are WF. This check takes
とる
place after all higher-ranked lifetimes have been instantiated. Checking the argument
引数
types ensures that the implied
暗黙の
bounds
制限する、結び付けられて
due to argument
引数
types are correct. Checking the return type ensures that the resulting
結果、戻り値
type of the call
呼び出し
is WF.

Method calls,

呼び出し
"UFCS" notation
記法
for fns and constants.
定数
These are the two ways to project a value out of a trait reference.
参照
A method call
呼び出し
or UFCS resolution will require that the trait reference
参照
is WF according
according to 〜に応じて
to the rules given
与えられた
above.

Normalizing associated type references.

参照
Whenever a projection type like T::Foo is normalized, we will require that the trait reference
参照
is WF.

Drawbacks

N/A

Alternatives
代わりのもの、選択肢

I'm not aware of any appealing alternatives.

代わりのもの、選択肢

Unresolved questions

Best policy for type aliases.

別名
The current policy is not to check type aliases,
別名
since they are transparent to type-checking, and hence their expansion can be checked instead. This is coherent, though somewhat confusing in terms
項、用語
of the interaction with projections, since we frequently cannot resolve
解決する
projections without at least minimal bounds
制限する、結び付けられて
(i.e., type IteratorAndItem<T:Iterator> = (T::Item, T)). Still, full-checking of WF on type aliases
別名
seems to just mean more annotation with little benefit. It might be nice to keep the current policy and later, if/when we adopt a more full notion of implied
暗黙の
bounds,
制限する、結び付けられて
rationalize it by saying that the suitable bounds
制限する、結び付けられて
for a type alias
別名
are implied
暗黙の
by its expansion.

For trait object type fragments, should we check WF conditions

条件
when we can? For example, if you have:

#![allow(unused)] fn main() { trait HashSet<K:Hash> }

should an object like Box<HashSet<NotHash>> be illegal? It seems like that would be inline with our "best effort" approach to bound

制限する、結び付けられて
regions, so probably yes.

Appendix

The informal explanation glossed over some details. This appendix tries to be a bit more thorough with how it is that we can conclude that a projection outlives 'a if its inputs outlive 'a. To start, let's specify

特定する、指定する、規定する
the projection <PROJ> as:

<P0 as Trait<P1...Pn>>::Id

where P can be a lifetime or type parameter

仮引数
as appropriate.

Then we know that there exists some impl of the form:

形式、形態、形作る

#![allow(unused)] fn main() { impl<X0..Xn> Trait<Q1..Qn> for Q0 { type Id = T; } }

Here again, X can be a lifetime or type parameter

仮引数
name, and Q can be any lifetime or type parameter.
仮引数

Let Θ be a suitable substitution

置き換え
[Xi => Ri] such that ∀i. Θ Qi == Pi (in other words, so that the impl applies
適用する
to the projection). Then the normalized form
形式、形態、形作る
of <PROJ> is Θ T. Note that because trait matching is invariant, the types must be exactly
正確に
equal.
等しい

RFC 447 and #24461 require that a parameter

仮引数
Xi can only appear
現れる
in T if it is constrained by the trait reference
参照
<Q0 as Trait<Q1..Qn>>. The full definition
定義
of constrained appears
現れる
below, but informally
非公式に
it means roughly that Xi appears
現れる
in Q0..Qn somewhere outside of a projection. Let's call
呼び出し
the constrained set
セットする、集合
of parameters
仮引数
Constrained(Q0..Qn).

Recall the rule OutlivesProjectionComponents:

OutlivesProjectionComponents: ∀i. R ⊢ Pi: 'a -------------------------------------------------- R ⊢ <P0 as Trait<P1..Pn>>::Id: 'a

We aim to show that ∀i. R Pi: 'a implies

暗黙の
R T): 'a, which implies
暗黙の
that this rule is a sound approximation for normalization. The argument
引数
follows
下記の、次に続く、追従する
from two lemmas ("proofs" for these lemmas are sketched below):

  1. First, we show that if R Pi: 'a, then every "subcomponent" P' of Pi outlives 'a. The idea here is that each variable
    変数、ストレージ
    Xi from the impl will match
    一致する、マッチさせる
    against and extract
    抽出する
    some subcomponent P' of Pi, and we wish to show that the subcomponent P' extracted
    抽出する
    by Xi outlives 'a.
  2. Then we will show that the type θ T outlives 'a if, for each of the in-scope parameters
    仮引数
    Xi, Θ Xi: 'a.

Definition

定義
1. Constrained(T) defines the set
セットする、集合
of type/lifetime parameters
仮引数
that are constrained by a type. This set
セットする、集合
is found just by recursing over and extracting
抽出する
all subcomponents except for those found in a projection. This is because a type like X::Foo does not constrain what type X can take
とる
on, rather it uses X as an input to compute a result:
結果、戻り値

Constrained(scalar) = {} Constrained(X) = {X} Constrained(&'x T) = {'x} | Constrained(T) Constrained(O0..On+'x) = Union(Constrained(Oi)) | {'x} Constrained([T]) = Constrained(T), Constrained(for<..> fn(T1..Tn) -> T0) = Union(Constrained(Ti)) Constrained(<P0 as Trait<P1..Pn>>::Id) = {} // empty set

Definition

定義
2. Constrained('a) = {'a}. In other words, a lifetime reference
参照
just constraints
制約
itself.

Lemma 1: Given

与えられた
R P: 'a, P = [X => P'] Q, and X Constrained(Q), then R P': 'a. Proceed
進む
by induction and by cases over the form
形式、形態、形作る
of P:

  1. If P is a scalar or parameter,
    仮引数
    there are no subcomponents, so P'=P.
  2. For nominal types, references,
    参照
    objects, and function types, either P'=P or P' is some subcomponent of P. The appropriate "outlives" rules all require that all subcomponents outlive 'a, and hence the conclusion follows
    下記の、次に続く、追従する
    by induction.
  3. If P' is a projection, that implies
    暗黙の
    that P'=P.
    • Otherwise,
      さもなければ
      Q must be a projection, and in that case, Constrained(Q) would be the empty
      空の
      set.
      セットする、集合

Lemma 2: Given

与えられた
that FV(T) X, ∀i. Ri: 'a, then [X => R] T: 'a. In other words, if all the type/lifetime parameters
仮引数
that appear
現れる
in a type outlive 'a, then the type outlives 'a. Follows
下記の、次に続く、追従する
by inspection of the outlives rules.

Edit History

RFC1592 - amend to require that tuple

タプル(複合型)
fields be sized