rustenumsreduce

finding max from Vec<Enum> - using reduce()?


I'm sure there is a better way to do this, but I do not know how to extract structure from Enum during reduce(). Assume ALL enums are identical in vector (but prob. solution needs to handle if this is not true also).

Simplest example:

#[derive(Debug)]
struct Data {
    d: u64,
}

#[derive(Debug)]
enum DataEnum {
    First(Data),
    Second,
}

fn main() {
    let v = vec![DataEnum::First(Data{d: 10}), DataEnum::First(Data{d: 5}), DataEnum::First(Data{d: 15}), DataEnum::First(Data{d: 13})];


    let mut vi = v.iter();
    let mut current = vi.next().unwrap();
    let mut max: u64;

    match current {
        DataEnum::First(data) => max = data.d,
        _ => panic!("dont know what to do with {:?}", current),
    }

    for next in vi {
        match next {
            DataEnum::First(data) => if max < data.d {
                current = next;
                max = data.d;
            },
            _ => panic!("dont know what to do with {:?}", next),
        }
    }

    println!("found max: {:?}", current);
}

Is there a better way to do this with fold or reduce?

Problem I have with reduce() is that I don't know how to handle the 'match' part inside closure properly (to extract and compare data.d).


Solution

  • You can certainly use reduce() in this kind of code. We start off by creating a utility function for just retrieving the data relevant for comparison, let's call it key:

    fn key(e: &DataEnum) -> u64 {
        match e {
            DataEnum::First(data) => data.d,
            _ => panic!("dont know what to do with {:?}", e),
        }
    }
    

    The function returns u64, but it could return a tuple or similar (which is what you'd use to properly support heterogeneous enums in your collection). With this function in place, you can use reduce like this:

    let max = v
        .iter()
        .reduce(|max, e| if key(e) > key(max) { e } else { max })
        .unwrap();
    
    println!("found max: {:?}", max);
    

    Playground

    However, Rust already provides an iterator method that determines its maximum by applying a key, conveniently called max_by_key, so you can just use that:

    let max = v.iter().max_by_key(|e| key(e)).unwrap();
    

    Playground