# Type inference
<!-- toc -->
Type inference is the process of automatic detection of the type of an
expression.
It is what allows Rust to work with fewer or no type annotations,
making things easier for users:
```rust
fn main() {
let mut things = vec![];
things.push("thing");
}
```
Here, the type of `things` is *inferred* to be `Vec<&str>` because of the value
we push into `things`.
The type inference is based on the standard Hindley-Milner (HM) type inference
algorithm, but extended in various ways to accommodate subtyping, region
inference, and higher-ranked types.
## A note on terminology
We use the notation `?T` to refer to inference variables, also called
existential variables.
We use the terms "region" and "lifetime" interchangeably. Both refer to
the `'a` in `&'a T`.
The term "bound region" refers to a region that is bound in a function
signature, such as the `'a` in `for<'a> fn(&'a u32)`. A region is
"free" if it is not bound.
## Creating an inference context
You create an inference context by doing something like
the following:
```rust,ignore
let infcx = tcx.infer_ctxt().build();
// Use the inference context `infcx` here.
```
`infcx` has the type `InferCtxt<'tcx>`, the same `'tcx` lifetime as on
the `tcx` it was built from.
The `tcx.infer_ctxt` method actually returns a builder, which means
there are some kinds of configuration you can do before the `infcx` is
created. See `InferCtxtBuilder` for more information.
<a id="vars"></a>
## Inference variables
The main purpose of the inference context is to house a bunch of
**inference variables** – these represent types or regions whose precise
value is not yet known, but will be uncovered as we perform type-checking.
If you're familiar with the basic ideas of unification from H-M type
systems, or logic languages like Prolog, this is the same concept. If
you're not, you might want to read a tutorial on how H-M type
inference works, or perhaps this blog post on
[unification in the Chalk project].
All told, the inference context stores five kinds of inference variables
(as of <!-- date-check --> March 2023):
- Type variables, which come in three varieties:
- General type variables (the most common). These can be unified with any
type.
- Integral type variables, which can only be unified with an integral type,
and arise from an integer literal expression like `22`.
- Float type variables, which can only be unified with a float type, and
arise from a float literal expression like `22.0`.
- Region variables, which represent lifetimes, and arise all over the place.
- Const variables, which represent constants.
All the type variables work in much the same way: you can create a new
type variable, and what you get is `Ty<'tcx>` representing an
unresolved type `?T`. Then later you can apply the various operations
that the inferencer supports, such as equality or subtyping, and it
will possibly **instantiate** (or **bind**) that `?T` to a specific
value as a result.
The region variables work somewhat differently, and are described
below in a separate section.
## Enforcing equality / subtyping
The most basic operations you can perform in the type inferencer is
**equality**, which forces two types `T` and `U` to be the same. The
recommended way to add an equality constraint is to use the `at`
method, roughly like so:
```rust,ignore
infcx.at(...).eq(t, u);
```
The first `at()` call provides a bit of context, i.e. why you are
doing this unification, and in what environment, and the `eq` method
performs the actual equality constraint.
When you equate things, you force them to be precisely equal. Equating
returns an `InferResult` – if it returns `Err(err)`, then equating
failed, and the enclosing `TypeError` will tell you what went wrong.
The success case is perhaps more interesting. The "primary" return
type of `eq` is `()` – that is, when it succeeds, it doesn't return a
value of any particular interest. Rather, it is executed for its