]> git.proxmox.com Git - rustc.git/blame - src/doc/rustc-dev-guide/src/generics.md
New upstream version 1.74.1+dfsg1
[rustc.git] / src / doc / rustc-dev-guide / src / generics.md
CommitLineData
781aab86 1# Generics and GenericArgs
74b04a01
XL
2
3Given a generic type `MyType<A, B, …>`, we may want to swap out the generics `A, B, …` for some
4other types (possibly other generics or concrete types). We do this a lot while doing type
5inference, type checking, and trait solving. Conceptually, during these routines, we may find out
6that one type is equal to another type and want to swap one out for the other and then swap that out
7for another type and so on until we eventually get some concrete types (or an error).
8
781aab86
FG
9In rustc this is done using [GenericArgsRef].
10Conceptually, you can think of `GenericArgsRef` as a list of types that are to be substituted for
11 the generic type parameters of the ADT.
74b04a01 12
781aab86 13`GenericArgsRef` is a type alias of `&'tcx List<GenericArg<'tcx>>` (see [`List` rustdocs][list]).
74b04a01 14[`GenericArg`] is essentially a space-efficient wrapper around [`GenericArgKind`], which is an enum
781aab86
FG
15indicating what kind of generic the type parameter is (type, lifetime, or const).
16Thus, `GenericArgsRef` is conceptually like a `&'tcx [GenericArgKind<'tcx>]` slice (but it is
17actually a `List`).
74b04a01 18
ba9703b0 19[list]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.List.html
781aab86
FG
20[`GenericArg`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.GenericArg.html
21[`GenericArgKind`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.GenericArgKind.html
22[GenericArgsRef]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.GenericArgsRef.html
74b04a01
XL
23
24So why do we use this `List` type instead of making it really a slice? It has the length "inline",
25so `&List` is only 32 bits. As a consequence, it cannot be "subsliced" (that only works if the
26length is out of line).
27
28This also implies that you can check two `List`s for equality via `==` (which would be not be
29possible for ordinary slices). This is precisely because they never represent a "sub-list", only the
30complete `List`, which has been hashed and interned.
31
32So pulling it all together, let’s go back to our example above:
33
34```rust,ignore
35struct MyStruct<T>
36```
37
38- There would be an `AdtDef` (and corresponding `DefId`) for `MyStruct`.
39- There would be a `TyKind::Param` (and corresponding `DefId`) for `T` (more later).
781aab86 40- There would be a `GenericArgsRef` containing the list `[GenericArgKind::Type(Ty(T))]`
74b04a01
XL
41 - The `Ty(T)` here is my shorthand for entire other `ty::Ty` that has `TyKind::Param`, which we
42 mentioned in the previous point.
781aab86 43- This is one `TyKind::Adt` containing the `AdtDef` of `MyStruct` with the `GenericArgsRef` above.
74b04a01
XL
44
45Finally, we will quickly mention the
ba9703b0 46[`Generics`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Generics.html) type. It
74b04a01
XL
47is used to give information about the type parameters of a type.
48
49### Unsubstituted Generics
50
51So above, recall that in our example the `MyStruct` struct had a generic type `T`. When we are (for
52example) type checking functions that use `MyStruct`, we will need to be able to refer to this type
53`T` without actually knowing what it is. In general, this is true inside all generic definitions: we
54need to be able to work with unknown types. This is done via `TyKind::Param` (which we mentioned in
55the example above).
56
57Each `TyKind::Param` contains two things: the name and the index. In general, the index fully
58defines the parameter and is used by most of the code. The name is included for debug print-outs.
59There are two reasons for this. First, the index is convenient, it allows you to include into the
60list of generic arguments when substituting. Second, the index is more robust. For example, you
61could in principle have two distinct type parameters that use the same name, e.g. `impl<A> Foo<A> {
62fn bar<A>() { .. } }`, although the rules against shadowing make this difficult (but those language
63rules could change in the future).
64
65The index of the type parameter is an integer indicating its order in the list of the type
66parameters. Moreover, we consider the list to include all of the type parameters from outer scopes.
67Consider the following example:
68
69```rust,ignore
70struct Foo<A, B> {
71 // A would have index 0
72 // B would have index 1
73
74 .. // some fields
75}
76impl<X, Y> Foo<X, Y> {
77 fn method<Z>() {
78 // inside here, X, Y and Z are all in scope
79 // X has index 0
80 // Y has index 1
81 // Z has index 2
82 }
83}
84```
85
86When we are working inside the generic definition, we will use `TyKind::Param` just like any other
87`TyKind`; it is just a type after all. However, if we want to use the generic type somewhere, then
88we will need to do substitutions.
89
90For example suppose that the `Foo<A, B>` type from the previous example has a field that is a
91`Vec<A>`. Observe that `Vec` is also a generic type. We want to tell the compiler that the type
92parameter of `Vec` should be replaced with the `A` type parameter of `Foo<A, B>`. We do that with
93substitutions:
94
95```rust,ignore
96struct Foo<A, B> { // Adt(Foo, &[Param(0), Param(1)])
97 x: Vec<A>, // Adt(Vec, &[Param(0)])
98 ..
99}
100
101fn bar(foo: Foo<u32, f32>) { // Adt(Foo, &[u32, f32])
102 let y = foo.x; // Vec<Param(0)> => Vec<u32>
103}
104```
105
106This example has a few different substitutions:
107
108- In the definition of `Foo`, in the type of the field `x`, we replace `Vec`'s type parameter with
109 `Param(0)`, the first parameter of `Foo<A, B>`, so that the type of `x` is `Vec<A>`.
110- In the function `bar`, we specify that we want a `Foo<u32, f32>`. This means that we will
111 substitute `Param(0)` and `Param(1)` with `u32` and `f32`.
112- In the body of `bar`, we access `foo.x`, which has type `Vec<Param(0)>`, but `Param(0)` has been
113 substituted for `u32`, so `foo.x` has type `Vec<u32>`.
114
115Let’s look a bit more closely at that last substitution to see why we use indexes. If we want to
116find the type of `foo.x`, we can get generic type of `x`, which is `Vec<Param(0)>`. Now we can take
781aab86
FG
117the index `0` and use it to find the right type substitution: looking at `Foo`'s `GenericArgsRef`,
118we have the list `[u32, f32]` , since we want to replace index `0`, we take the 0-th index of this
74b04a01
XL
119list, which is `u32`. Voila!
120
121You may have a couple of followup questions…
122
781aab86 123 **`type_of`** How do we get the "generic type of `x`"? You can get the type of pretty much anything
74b04a01
XL
124 with the `tcx.type_of(def_id)` query. In this case, we would pass the `DefId` of the field `x`.
125 The `type_of` query always returns the definition with the generics that are in scope of the
126 definition. For example, `tcx.type_of(def_id_of_my_struct)` would return the “self-view” of
127 `MyStruct`: `Adt(Foo, &[Param(0), Param(1)])`.
128
781aab86
FG
129How do we actually do the substitutions? There is a function for that too! You
130use [`instantiate`] to replace a `GenericArgsRef` with another list of types.
74b04a01 131
781aab86
FG
132[Here is an example of actually using `instantiate` in the compiler][instantiatex].
133The exact details are not too important, but in this piece of code, we happen to be
134converting from the `rustc_hir::Ty` to a real `ty::Ty`. You can see that we first get some args
135(`args`). Then we call `type_of` to get a type and call `ty.instantiate(tcx, args)` to get a new
136version of `ty` with the args made.
74b04a01 137
781aab86
FG
138[`instantiate`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/generic_args/struct.EarlyBinder.html#method.instantiate
139[instantiatex]: https://github.com/rust-lang/rust/blob/8a562f9671e36cf29c9c794c2646bcf252d55535/compiler/rustc_hir_analysis/src/astconv/mod.rs#L905-L927
74b04a01
XL
140
141**Note on indices:** It is possible for the indices in `Param` to not match with what we expect. For
142example, the index could be out of bounds or it could be the index of a lifetime when we were
143expecting a type. These sorts of errors would be caught earlier in the compiler when translating
144from a `rustc_hir::Ty` to a `ty::Ty`. If they occur later, that is a compiler bug.