rust: write more readable code with type alias

Amay B
CoderHack.com
Published in
3 min readSep 22, 2023
Photo by Angèle Kamp on Unsplash

Type aliases in Rust allow you to assign a new name to an existing type. They are defined using the type keyword. The syntax is:

type <alias-name> = <type>

For example, you can create a type alias for a complex type like:

type Result<T> = ::std::result::Result<T, ::std::io::Error>;

Now Result<T> can be used instead of the longer ::std::result::Result<T, ::std::io::Error> type.

However, type aliases do not create a new type. They simply introduce a new name for an existing type. This means that type aliases share the same underlying type and act exactly the same as the original type.

Use cases for type aliases

Type aliases are useful in several cases:

  1. Simplifying complex type signatures: As seen in the above example, type aliases can be used to simplify long, complex type signatures by giving them a shorter name.
  2. Abstraction over concrete types: Type aliases can be used to abstract over concrete types. For example:
type File = ::std::fs::File;

Now File can be used instead of ::std::fs::File throughout the codebase. If the concrete type changes in the future, only the type alias definition needs to be updated.

  1. Readability: Type aliases can be used to improve readability of types by giving them descriptive names. For example:
type UserId = u32;

is more readable than just using u32 directly.

Generic type aliases

Type aliases can also be generic, by using generic type parameters. The syntax is:

type <alias-name><T> = <generic-type-def>

For example, a generic type alias for Vec would be:

type List<T> = Vec<T>;

Now List<T> can be used as a generic type alias instead of the concrete Vec<T> type. For example:

let ints: List<i32> = vec![1, 2, 3];
let strings: List<String> = vec!["Hello".to_string(), "World!".to_string()];

We can also have multi-parameter generic type aliases. For example:

type Grid<T> = Vec<Vec<T>>;

And use it as:

let grid = Grid::<u32>(vec![vec![1, 2, 3], vec![4, 5, 6]]);

Associated type aliases in traits

Traits can have associated type aliases which define placeholder types that are implemented by implementors of the trait. The syntax is:

trait <trait-name> {
type <associated-type-alias> associated type;
}

For example, the Iterator trait has an associated type Item which represents the type of elements being iterated over:

trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
}

Types that implement the Iterator trait need to specify the concrete type for Item. For example:

struct MyIterator { /* ... */  } 
impl Iterator for MyIterator {
type Item = usize;
fn next(&mut self) -> Option<usize> { /* ... */ }
}

Here MyIterator specifies that for this iterator, the associated type Item is usize.

Advanced uses of type aliases

Type aliases have some advanced use cases in Rust:

  1. Opaque types: Type aliases can be used to define opaque types that hide the concrete underlying type. For example:

type List = impl Node;
fn get_node(id: NodeId) -> Node { ... }

`impl Trait` in type aliases is unstable
see issue #63063 <https://github.com/rust-lang/rust/issues/63063>

Here, List is an opaque type alias for Node. The concrete type is abstracted away and users cannot depend on it being a Node.

  1. Newtypes: Type aliases can be used to create newtypes that introduce a type level distinction between two types that share the same underlying type. For example:
type Inches = u32;  
let len = Inches(10);

Here Inches is a newtype around u32 that distinguishes Inches from a raw u32 at the type level, even though they share the same representation.

  1. Phantom types: A phantom type parameter is one that doesn’t show up at runtime, but is checked statically (and only) at compile time. For example:

use std::marker::PhantomData;
#[derive(PartialEq)] // Allow equality test for this type.
struct PhantomTuple<A, B>(A, PhantomData<B>);

fn main() {
let _tuple1: PhantomTuple<char, f32> = PhantomTuple('Q', PhantomData);
}

Here Phantom<A, B> is a phantom type that carries the State information at the type level, even though it has no runtime representation.

Conclusion

Type aliases are a useful feature of Rust to name existing types, abstract over concrete types, improve readability, define generic type aliases, associated types in traits, and enable advanced use cases like opaque types, newtypes and phantom types. They allow giving meaningful names to types and carrying information at the type level. Overall, type aliases are an important tool in Rust’s type system arsenal.

--

--