Reducing match indentation for deeply nested properties

天涯浪子 提交于 2021-01-28 18:29:50

问题


I need to refer to a value deep within a structure which includes an Option nested in a struct property, nested in a Result.

My current (working) solution is:

    let raw = &packet[16..];
    match PacketHeaders::from_ip_slice(raw) {
        Err(_value) => {
            /* ignore */
        },
        Ok(value) => {
            match value.ip {
                Some(Version4(header)) => {
                    let key = format!("{}.{}.{}.{},{}.{}.{}.{}", 
                        header.source[0], header.source[1], header.source[2], header.source[3],
                        header.destination[0], header.destination[1], header.destination[2], header.destination[3],
                    );

                    let Count {packets, bytes} = counts.entry(key).or_insert(Count {packets: 0, bytes: 0});
                    *packets += 1;
                    *bytes += packet.len();

                    if p > 1000 { /* exit after 1000 packets */
                        for (key, value) in counts {
                            println!("{},{},{}", key, value.packets, value.bytes);
                        }
                        return ();
                    }
                    p += 1;
                }
                _ => {
                    /* ignore */
                }
            }
        }
    }

(The problem with my current code is the excessive nesting and the two matches.)

All I want is PacketHeaders::from_ip_slice(ip) >> Ok >> ip >> Some >> Version4.

How can I get this, or ignore a failure nicely (NOT crash/exit) for each captured packet?


回答1:


Pattern matching nests, and the pattern for struct looks like struct literals with the addition that .. means "and match all the rest, which I don't care about", similar to how the _ pattern means "match anything". Meaning you can do

match PacketHeaders::from_ip_slice(raw) {
    Ok(PacketHeaders { ip: Version4(header), .. }) => {
        /* handle */
    }
    _ => {
        /* ignore `Err(_)` and other `Ok(_)` */
    },
}

and if you're going to ignore all but one case, you can use if let:

if let Ok(PacketHeaders { ip: Version4(header), .. }) = PacketHeaders::from_ip_slice(raw) {
    /* handle */
}



回答2:


Code ended up being:

    let raw = &packet[16..];
    if let Ok(PacketHeaders { ip: Some(Version4(header)), link: _, vlan: _, transport: _, payload: _}) = PacketHeaders::from_ip_slice(raw) {
        let key = format!("{}.{}.{}.{},{}.{}.{}.{}", 
            header.source[0], header.source[1], header.source[2], header.source[3],
            header.destination[0], header.destination[1], header.destination[2], header.destination[3],
        );

        let Count {packets, bytes} = counts.entry(key).or_insert(Count {packets: 0, bytes: 0});
        *packets += 1;
        *bytes += packet.len();
    }


来源:https://stackoverflow.com/questions/62469920/reducing-match-indentation-for-deeply-nested-properties

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!