udoprog.github.io

Advent of Rust Day 6 - The power of iterators

This is a series where I’ll be discussing interesting Rust tidbits that I encounter when solving Advent of Code 2017.

You can find the complete (spoiler) solution here: udoprog/rust-advent-of-code-2017

On day 6 we need to iterate over a vector, by starting at an index in the middle of the vector and wrap around.

A neat way to accomplish this, is to use range expressions (like 0..10) as an iterator, and split it into two parts.

let mut v = vec![0, 0, 0, 0];
let idx = 2;

for (i, idx) in (idx..v.len()).chain(0usize..idx).enumerate() {
    v[idx] += i;
}

println!("out = {:?}", v);

This would print:

out = [2, 3, 0, 1]

Advent of Rust Day 5 - Have your bounds and eat them too!

This is a series where I’ll be discussing interesting Rust tidbits that I encounter when solving Advent of Code 2017.

You can find the complete (spoiler) solution here: udoprog/rust-advent-of-code-2017

Attempting to access data that is out of bounds in a collections could potentially ruin your day.

To combat this, rust provides ‘safe’ alternatives for slice indexing in the form of get and get_mut.

Note: these are available on Vec because it implements Deref<Target = [T]>, which causes rust to look for methods there as well.

These return an Option<&T> and Option<&mut T>, requiring you to check that the provided index was present, before attempting to deal with the data.

So this:

let mut v = vec![1, 2, 2];
println!("data = {}", v[2]);

Becomes this:

let mut v = vec![1, 2, 2];

match v.get(2) {
  Some(value) => println!("data = {}", value),
  None => println!("no value present :("),
}

Advent of Rust Day 4 - Testing things that read stuff

This is a series where I’ll be discussing interesting Rust tidbits that I encounter when solving Advent of Code 2017.

You can find the complete (spoiler) solution here: udoprog/rust-advent-of-code-2017

If you want to test a function that reads stuff, you can do the following:

use std::io::Read;

fn my_fun<R: Read>(reader: R) {
    /*  */
}

This allows you to write tests like these:

#[test]
fn test_with_cursor() {
    use std::io::Cursor;
    my_fun(Cursor::new("hello\nworld"));
}

If you don’t like monomorphization (large binaries?), use a trait object instead:

use std::io::Read;

fn my_fun(reader: &mut Read) {
    /*  */
}

But pay the price of dynamic dispatch.

Advent of Rust Day 3 - Option as an Iterator

This is a series where I’ll be discussing interesting Rust tidbits that I encounter when solving Advent of Code 2017.

You can find the complete (spoiler) solution here: udoprog/rust-advent-of-code-2017

Option can be used as an Iterator:

fn cell_value(storage: &HashMap<(i64, i64), u64>, x: i64, y: i64) -> u64 {
    [
        &(x - 1, y - 1), &(x  , y - 1), &(x + 1, y - 1),
        &(x - 1, y    ), /* (x, y)   */ &(x + 1, y    ),
        &(x - 1, y + 1), &(x  , y + 1), &(x + 1, y + 1),
    ].into_iter().flat_map(|k| storage.get(k)).map(|v| *v).sum()
}

Note that storage.get(k) returns an Option, And the flat_map closure returns U: IntoIterator.

Option implements IntoIterator, which for None is an empty iterator, and Some(T) is an iterator with a single item. You can see that in action for the Item struct which is used to implement this behavior: https://doc.rust-lang.org/src/core/option.rs.html#912

Advent of Rust Day 1 - Everything has a scope

This is a series where I’ll be discussing interesting Rust tidbits that I encounter when solving Advent of Code 2017.

You can find the complete (spoiler) solution here: udoprog/rust-advent-of-code-2017

I decided the use the following compact expression to open and read the entire contents of a file:

let mut data = String::new();
File::open(path)?.read_to_string(&mut data)?;

Desugared, it would read as something like this:

let mut data = String::new();

{
    let mut _f = File::open(path)?;
    _f.read_to_string(&mut data)?;
}

This means that the file is closed immediately after its contents has been read, and is not accessible anywhere else in the scope.