Search code examples
rustpattern-matchingip-address

Why cant I use a constant when pattern matching even though it implements PartialEq and Eq?


I want to filter out the IPv4 localhost address by using the constant net::Ipv4Addr::LOCALHOST when pattern matching:

use get_if_addrs; // 0.5.3
use std::net;

fn main() -> std::io::Result<()> {
    assert_eq!(
        "127.0.0.1".parse::<net::Ipv4Addr>().unwrap(),
        net::Ipv4Addr::LOCALHOST
    );
    {
        let ifaces = get_if_addrs::get_if_addrs().unwrap();
        for iface in ifaces {
            match iface.addr {
                get_if_addrs::IfAddr::V4(get_if_addrs::Ifv4Addr {
                    ip: _,
                    netmask: _,
                    broadcast: None,
                }) => (),
                get_if_addrs::IfAddr::V4(get_if_addrs::Ifv4Addr {
                    ip: net::Ipv4Addr::LOCALHOST,
                    netmask: _,
                    broadcast: _,
                }) => (),
                get_if_addrs::IfAddr::V4(addr) => println!("{:?}", addr),
                get_if_addrs::IfAddr::V6(_) => (),
            }
        }
    }
    Ok(())
}

I get an error

error: to use a constant of type `std::net::Ipv4Addr` in a pattern, `std::net::Ipv4Addr` must be annotated with `#[derive(PartialEq, Eq)]`
  --> src/main.rs:19:25
   |
19 |                     ip: net::Ipv4Addr::LOCALHOST,
   |                         ^^^^^^^^^^^^^^^^^^^^^^^^

warning: unreachable pattern
  --> src/main.rs:23:17
   |
23 |                 get_if_addrs::IfAddr::V4(addr) => println!("{:?}", addr),
   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: #[warn(unreachable_patterns)] on by default

std::net::Ipv4Addr does have implementation for PartialEq and Eq, so what does that error mean? How do I solve it?


Solution

  • As the error message states:

    must be annotated with #[derive(PartialEq, Eq)]

    This is not true for Ipv4Addr, which implements it manually, instead of deriving it.

    Instead, use a match guard:

    use get_if_addrs; // 0.5.3
    
    fn main() -> std::io::Result<()> {
        let ifaces = get_if_addrs::get_if_addrs().unwrap();
        for iface in ifaces {
            match iface.addr {
                get_if_addrs::IfAddr::V4(get_if_addrs::Ifv4Addr {
                    broadcast: None, ..
                }) => (),
                get_if_addrs::IfAddr::V4(get_if_addrs::Ifv4Addr { ip, .. }) if ip.is_loopback() => (),
                get_if_addrs::IfAddr::V4(addr) => println!("{:?}", addr),
                get_if_addrs::IfAddr::V6(_) => (),
            }
        }
    
        Ok(())
    }
    

    You may also consider introducing some nesting:

    use get_if_addrs::{IfAddr, Ifv4Addr}; // 0.5.3
    
    fn main() -> std::io::Result<()> {
        let ifaces = get_if_addrs::get_if_addrs().unwrap();
        for iface in ifaces {
            match iface.addr {
                IfAddr::V4(addr) => match addr {
                    Ifv4Addr {
                        broadcast: None, ..
                    } => (),
                    Ifv4Addr { ip, .. } if ip.is_loopback() => (),
                    addr => println!("{:?}", addr),
                },
                IfAddr::V6(_) => (),
            }
        }
    
        Ok(())
    }
    

    RFC 1445 explains the underlying decisions more:

    • Introduce a feature-gated attribute #[structural_match] which can be applied to a struct or enum T to indicate that constants of type T can be used within patterns.
    • Have #[derive(Eq)] automatically apply this attribute to the struct or enum that it decorates. Automatically inserted attributes do not require use of feature-gate.
    • When expanding constants of struct or enum type into equivalent patterns, require that the struct or enum type is decorated with #[structural_match]. Constants of builtin types are always expanded.

    The practical effect of these changes will be to prevent the use of constants in patterns unless the type of those constants is either a built-in type (like i32 or &str) or a user-defined constant for which Eq is derived (not merely implemented).