DEV Community

Ryan Palo
Ryan Palo

Posted on • Originally published at assertnotmagic.com

5

Type Aliases in Rust

I always forget about type aliases when I'm writing Rust code--mostly I get caught up in making it work mechanically and making the compiler happy and forget about readability. But yesterday, I was working through Advent of Code Day 3 challenge, and I remembered them. Just to see if they helped, I sprinkled them into my code, and I was shocked at how much better I felt about reading through what I had done.

Better readability, better self-documentation, less static to hold in mental memory at once.

Here's a snippet of the code before the transformation:

fn parse_input() -> (Vec<Coordinate>, Vec<Coordinate>) {
    let text = fs::read_to_string("data/day3.txt").unwrap();
    let lines: Vec<&str> = text.split("\n").collect();
    let mut wires = lines.into_iter().map(build_moves).map(make_wire);
    (wires.next().unwrap(), wires.next().unwrap())
}

/// Build a Wire out of relative Moves
fn make_wire(moves: Vec<Coordinate>) -> Vec<Coordinate> {
    let mut current = Coordinate { x: 0, y: 0 };
    let mut results: Wire = Vec::new();

    for step in moves {
        current = step + current;
        results.push(current);
    }

    results
}

// ...
Enter fullscreen mode Exit fullscreen mode

Lots of Vectors full of Coordinates right? And some are global coordinates, and some are lists of relative steps from one coordinate to the next, so they're not conceptually the same "type" of Vec<Coordinate>. Messy, right?

Here's what it looks like after I add in my type aliases:

/// A Wire is a chain of coordinates that are electrically connected
type Wire = Vec<Coordinate>;

/// Moves are an ordered list of delta moves to make to form a wire
type Moves = Vec<Coordinate>;


fn parse_input() -> (Wire, Wire) {
    let text = fs::read_to_string("data/day3.txt").unwrap();
    let lines: Vec<&str> = text.split("\n").collect();
    let mut wires = lines.into_iter().map(build_moves).map(make_wire);
    (wires.next().unwrap(), wires.next().unwrap())
}

/// Build a Wire out of relative Moves
fn make_wire(moves: Moves) -> Wire {
    let mut current = Coordinate { x: 0, y: 0 };
    let mut results: Wire = Vec::new();

    for step in moves {
        current = step + current;
        results.push(current);
    }

    results
}

// ...
Enter fullscreen mode Exit fullscreen mode

Now, even though Wire and Moves are made up of the same ingredients, they are separated conceptually for our brains to process, and our intent is documented much better.

This is my fourth attempt at learning Rust, and it's certainly feeling quite a bit better than any time before. So, yay for improvement!

Image of Timescale

🚀 pgai Vectorizer: SQLAlchemy and LiteLLM Make Vector Search Simple

We built pgai Vectorizer to simplify embedding management for AI applications—without needing a separate database or complex infrastructure. Since launch, developers have created over 3,000 vectorizers on Timescale Cloud, with many more self-hosted.

Read full post →

Top comments (3)

Collapse
 
koehr profile image
Norman

Really good reminder and a nice and simple example. Thanks!

Collapse
 
coolshaurya profile image
Shaurya

Great post! I didn't know about type aliases in rust

Collapse
 
rpalo profile image
Ryan Palo

Thanks! Yeah they are great 👍🏻

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more