]> git.proxmox.com Git - rustc.git/blame - src/doc/rustc-dev-guide/src/early-late-bound.md
New upstream version 1.69.0+dfsg1
[rustc.git] / src / doc / rustc-dev-guide / src / early-late-bound.md
CommitLineData
6a06907d
XL
1# Early and Late Bound Variables
2
3In Rust, item definitions (like `fn`) can often have generic parameters, which
4are always [_universally_ quantified][quant]. That is, if you have a function
5like
6
7```rust
8fn foo<T>(x: T) { }
9```
10
11this function is defined "for all T" (not "for some specific T", which would be
12[_existentially_ quantified][quant]).
13
14[quant]: ./appendix/background.md#quantified
15
16While Rust *items* can be quantified over types, lifetimes, and constants, the
17types of values in Rust are only ever quantified over lifetimes. So you can
18have a type like `for<'a> fn(&'a u32)`, which represents a function pointer
19that takes a reference with any lifetime, or `for<'a> dyn Trait<'a>`, which is
20a `dyn` trait for a trait implemented for any lifetime; but we have no type
21like `for<T> fn(T)`, which would be a function that takes a value of *any type*
22as a parameter. This is a consequence of monomorphization -- to support a value
23of type `for<T> fn(T)`, we would need a single function pointer that can be
24used for a parameter of any type, but in Rust we generate customized code for
25each parameter type.
26
3c0e092e 27One consequence of this asymmetry is a weird split in how we represent some
6a06907d
XL
28generic types: _early-_ and _late-_ bound parameters.
29Basically, if we cannot represent a type (e.g. a universally quantified type),
30we have to bind it _early_ so that the unrepresentable type is never around.
31
32Consider the following example:
33
34```rust,ignore
35fn foo<'a, 'b, T>(x: &'a u32, y: &'b T) where T: 'b { ... }
36```
37
38We cannot treat `'a`, `'b`, and `T` in the same way. Types in Rust can't have
39`for<T> { .. }`, only `for<'a> {...}`, so whenever you reference `foo` the type
40you get back can't be `for<'a, 'b, T> fn(&'a u32, y: &'b T)`. Instead, the `T`
41must be substituted early. In particular, you have:
42
43```rust,ignore
44let x = foo; // T, 'b have to be substituted here
45x(...); // 'a substituted here, at the point of call
46x(...); // 'a substituted here with a different value
47```
48
49## Early-bound parameters
50
51Early-bound parameters in rustc are identified by an index, stored in the
52[`ParamTy`] struct for types or the [`EarlyBoundRegion`] struct for lifetimes.
53The index counts from the outermost declaration in scope. This means that as you
54add more binders inside, the index doesn't change.
55
56For example,
57
58```rust,ignore
59trait Foo<T> {
60 type Bar<U> = (Self, T, U);
61}
62```
63
64Here, the type `(Self, T, U)` would be `($0, $1, $2)`, where `$N` means a
65[`ParamTy`] with the index of `N`.
66
67In rustc, the [`Generics`] structure carries this information. So the
68[`Generics`] for `Bar` above would be just like for `U` and would indicate the
69'parent' generics of `Foo`, which declares `Self` and `T`. You can read more
70in [this chapter](./generics.md).
71
72[`ParamTy`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.ParamTy.html
73[`EarlyBoundRegion`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.EarlyBoundRegion.html
74[`Generics`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Generics.html
75
76## Late-bound parameters
77
78Late-bound parameters in `rustc` are handled quite differently (they are also
79specialized to lifetimes since, right now, only late-bound lifetimes are
80supported, though with GATs that has to change). We indicate their potential
81presence by a [`Binder`] type. The [`Binder`] doesn't know how many variables
82there are at that binding level. This can only be determined by walking the
83type itself and collecting them. So a type like `for<'a, 'b> ('a, 'b)` would be
84`for (^0.a, ^0.b)`. Here, we just write `for` because we don't know the names
85of the things bound within.
86
87Moreover, a reference to a late-bound lifetime is written `^0.a`:
88
89- The `0` is the index; it identifies that this lifetime is bound in the
90 innermost binder (the `for`).
91- The `a` is the "name"; late-bound lifetimes in rustc are identified by a
92 "name" -- the [`BoundRegionKind`] enum. This enum can contain a
93 [`DefId`][defid] or it might have various "anonymous" numbered names. The
94 latter arise from types like `fn(&u32, &u32)`, which are equivalent to
95 something like `for<'a, 'b> fn(&'a u32, &'b u32)`, but the names of those
96 lifetimes must be generated.
97
98This setup of not knowing the full set of variables at a binding level has some
99advantages and some disadvantages. The disadvantage is that you must walk the
100type to find out what is bound at the given level and so forth. The advantage
101is primarily that, when constructing types from Rust syntax, if we encounter
102anonymous regions like in `fn(&u32)`, we just create a fresh index and don't have
103to update the binder.
104
105[`Binder`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Binder.html
106[`BoundRegionKind`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.BoundRegionKind.html
107[defid]: ./hir.html#identifiers-in-the-hir