Search code examples
rustiteratorclosures

Issue with summing over map closure


I'm working on AoC to learn Rust and have been trying to cobble together a solution based on what I've seen more experienced rust programmers write. Below seems to compile entirely except I have an issue with the .sum(). The compiler states that Iterator::Item changed to Result<usize, _> just before the sum operation.

I don't know how I wouldn't use a Result type from get_priority though. As far as I can tell sum works on iterators but it doesn't see the result type as being an iterator?

fn main() {
    
    let data = fs::read_to_string("data/data.txt").expect("File should exist");
    let lines = data.lines(); // this is an iterator of lines (type &str)

    let result: usize = lines.map(|rucksack| {
        let (cmprt1, cmprt2) = rucksack.split_at(rucksack.len() / 2);
        let cmprt1: ByteSet = cmprt1.into();
        let cmprt2: ByteSet = cmprt2.into();
        let dup = cmprt1.intersection(cmprt2).first().ok_or("Error finding intersection")?;
        Ok(get_priorty(dup)?)
    }).sum();    
    println!("{}", result)
}

fn get_priorty(letter: u8) -> Result<usize, Box<dyn Error>>{
    match letter {
        b'a'..=b'z' => Ok(1 - (letter + b'a') as usize),
        b'A'..=b'Z' => Ok(27 - (letter + b'A') as usize),
        _ => Err("Unexpected character in rucksack".into())
    }
}

The error message

10   |       let result: usize = lines.map(|rucksack| {
     |  _______________________________^
11   | |         let (cmprt1, cmprt2) = rucksack.split_at(rucksack.len() / 2);
12   | |         let cmprt1: ByteSet = cmprt1.into();
13   | |         let cmprt2: ByteSet = cmprt2.into();
14   | |         let dup = cmprt1.intersection(cmprt2).first().ok_or("Error finding intersection")?;
15   | |         Ok(get_priorty(dup)?)
16   | |     }).sum();    
     | |______^ `Iterator::Item` changed to `Result<usize, _>` here
note: required by a bound in `std::iter::Iterator::sum`

Solution

  • When you're summing items of type Result<usize, E> you get a result of the same type. But here you try to force the result to a plain usize. You need to either:

    • Give the correct type let result: Result<usize, Box<dyn Error>> = lines.map(|rucksack| { … }).sum();
    • Remove the type annotation and let the compiler infer the correct type: let result = lines.map(|rucksack| { … }).sum();
    • Or handle the error: let result: usize = lines.map(|rucksack| { … }).sum().unwrap();