Advent of Rust // Day 4

The plural of regex is regrets, some people say, but I like ‘em. Not for everything and all the time but they come in handy for some tasks. Like this, for example:

fn line_to_sections_pair(line: &str) -> (Sections, Sections) {  
    let re = Regex::new(r"^(\d+)-(\d+),(\d+)-(\d+)$").unwrap();  
    let captures = re.captures(line).unwrap();  
  
    return (  
        Sections::new(captures[1].parse().unwrap(), captures[2].parse().unwrap()),  
        Sections::new(captures[3].parse().unwrap(), captures[4].parse().unwrap())  
    );  
}

Sections in the code above, in case you’re wondering, is just a type alias for RangeInclusive<u32>. Both parts of the puzzle had essentially the same task: to count the number of pairs that match a certain criterion.

fn number_of_matching_pairs(input: Vec<&str>, filter: fn(a: &Sections, b: &Sections) -> bool) -> u32 {  
    return input.iter()  
        .map(|l| line_to_sections_pair(l))  
        .filter(|(a, b)| filter(a, b))  
        .count() as u32;  
}

For part one, either of the two sections must be contained in the other:

fn is_either_contained(a: &Sections, b: &Sections) -> bool {  
    return (b.contains(a.start()) && b.contains(a.end())) ||  
        (a.contains(b.start()) && a.contains(b.end()));  
}

For part two, both sections must have an overlap:

fn is_overlapping(a: &Sections, b: &Sections) -> bool {  
    return b.contains(a.start()) || b.contains(a.end()) ||  
        a.contains(b.start()) || a.contains(b.end());  
}

Plugged together:

fn part_one(input: Vec<&str>) -> u32 {  
    return number_of_matching_pairs(input, is_either_contained);  
}  
  
fn part_two(input: Vec<&str>) -> u32 {  
    return number_of_matching_pairs(input, is_overlapping);  
}  
  
fn main() {  
    let input = fs::read_to_string("input/day04.txt").unwrap();  
  
    println!("Part One: {}", part_one(input.lines().collect()));  
    println!("Part Two: {}", part_two(input.lines().collect()));  
}

I went deeper down the rabbit hole of testing in Rust and was looking for ways to do parameterized tests. The first thing I discovered was rstest and I gave it a try. It worked like a charm for what I wanted to do.

#[rstest]  
#[case(1..=2, 3..=4, false)]  
#[case(5..=6, 3..=4, false)]  
#[case(1..=3, 3..=4, true)]  
#[case(1..=5, 3..=4, true)]  
fn is_overlapping_test(#[case] a: Sections, #[case] b: Sections, #[case] expected: bool) {  
    assert_eq!(  
        expected,  
        is_overlapping(&a, &b),  
        "{a:?} {} overlap with {b:?}", if expected { "should" } else { "should not" }  
    );  
}

The full source and solutions for the others puzzles of Advent of Code can be found in my GitHub repository.

See also in Advent of Rust

Comments

You can leave a comment by replying to this post on Mastodon.