为什么我在模式匹配时不能使用常量,即使它实现了 PartialEq 和 Eq?

Sea*_*act 2 ip-address pattern-matching rust

我想net::Ipv4Addr::LOCALHOST在模式匹配时使用常量过滤掉 IPv4 localhost 地址:

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(())
}
Run Code Online (Sandbox Code Playgroud)

我收到一个错误

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(())
}
Run Code Online (Sandbox Code Playgroud)

std::net::Ipv4Addr确实有PartialEqand 的实现Eq,那么这个错误是什么意思?我该如何解决?

She*_*ter 5

正如错误消息所述:

必须注释 #[derive(PartialEq, Eq)]

不适用于Ipv4Addr,它手动实现它,而不是派生它。

相反,使用匹配守卫

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(())
}
Run Code Online (Sandbox Code Playgroud)

你也可以考虑引入一些嵌套:

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(())
}
Run Code Online (Sandbox Code Playgroud)

RFC 1445更详细地解释了基本决策:

  • 引入#[structural_match]可应用于结构或枚举的功能门控属性,T以指示T 可以在模式中使用类型的常量。
  • #[derive(Eq)]自动将此属性应用于它装饰的结构或枚举。自动插入的属性不需要使用特征门。
  • 将 struct 或 enum 类型的常量扩展为等效模式时,要求 struct 或 enum 类型用 #[structural_match]. 内置类型的常量总是被扩展的。

这些变化的实际效果会防止模式的使用的常数的,除非这些常数的类型是一个内置型(如i32&str),或者用户定义的常数,其 Eq衍生(不仅仅是实现)。