Type

From Rust Community Wiki
(Redirected from Type alias)
Jump to navigation Jump to search

A type is a classification of values. Values of the same type have a similar meaning and purpose, and support the same operations. Values of different types can have the same memory representation – for example, both u8 and bool can be represented by a zero, but because they are different types, the zero has a different meaning.

In Rust, types are enforced at compile time to prevent logic errors. However, since memory safety invariants are encoded in the type system, it also prevents memory safety bugs.

Types in Rust can be divided into:

Types can be generic. Generic types are sometimes called type constructors. For example, Box<T> is generic over T. By substituting T with a type, new types such as Box<bool> or Box<String> can be created.

Traits aren't types, but they categorize types with certain properties or functionality.

Example[edit | edit source]

fn function(x: i32) -> u32 {
// type of x ~~^^^     ^^^ return type

    // type conversion:
    let y = x as u32;
    
    // the type of 5 is inferred:
    y + 5
}

Implementing a type[edit | edit source]

After declaring a type, impl blocks can be used to add functionality to it. These are called inherent impls, and functions within them are called inherent functions (or methods):

// declare a struct type:
pub struct Foo;

// an inherent impl:
impl Foo {
    // an inherent function:
    pub fn new() -> Self {
        Foo
    }
}

Types can have multiple inherent impl blocks (except primitive types). Unlike trait functions, inherent functions can have a visibility modifier. Inherent impls must always be in the same crate as the type they implement.

Nominal and structural types[edit | edit source]

Types can be divided into nominal types (types with a name) and structural types. Only nominal types can have inherent impl blocks.

Nominal types include:

All other types are structural:

Type aliases[edit | edit source]

Type aliases don't create a new type, just a new name to refer to a type. Different aliases that refer to the same type can be used interchangeably. Type aliases can be generic:

struct Foo<T>(T);

type MaybeFoo<T> = Result<Foo<T>, ()>;

// with a generic error type that defaults to String:
type FooResult<T, Err = String> = Result<Foo<T>, Err>;

Type inference[edit | edit source]

Types of parameters and the return type of functions need to be specified explicitly. In other places, types can often be automatically inferred.

The compiler does this by giving values whose type isn't known an {unknown} type placeholder. Then the surrounding context is used to replace the type placeholders with actual types.

Example[edit | edit source]

Let's look at an example where a lot of type inference takes place, and see how the compiler approaches it:

fn foo(iter: impl IntoIterator<Item = i32>) {
    // what is x?
    let x: Vec<_> = iter
        .into_iter()
        .map(|n| n * 2)
        .collect();
}

We don't know the type of iter, but we know that it implements the IntoIteratorThis links to official Rust documentation trait. IntoIterator::into_iter() returns Self::IntoIter, which implements Iterator<Item = Self::Item>This links to official Rust documentation.

Since the Item is i32, this tells the compiler that n in line 5 is an i32. Since i32 implements Mul<Rhs = Self>This links to official Rust documentation, Rust also infers the type i32 for the 2 literal. The return type is Self::OutputThis links to official Rust documentation, which also happens to be i32.

Iterator::map()This links to official Rust documentation accepts a closure and returns a type which implements Iterator. The Item of the iterator is the return type of the closure (i32).

Iterator::collect()This links to official Rust documentation returns a generic type B that implements FromIterator<Self::Item>This links to official Rust documentation. The compiler knows that the return type must be a VecThis links to official Rust documentation, so it looks for implementations of FromIterator<Self::Item> for Vec, and it finds impl<T> FromIterator<T> for Vec<T>. This means that the type in the Vec<_> is the same type as Self::Item, so Rust correctly infers the type Vec<i32> for x.