当输出值不是类型的一部分时,如何实现std :: ops :: Index

lit*_*ude 2 operator-overloading traits lifetime rust

我试图实现一个基本上包装的IP地址类型u32:

#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Address(u32);
Run Code Online (Sandbox Code Playgroud)

我执行std::ops的是有意义的一个IP地址(运营商&,|,+,-,等).唯一造成麻烦的是std::ops::Index:

#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Address(u32);

enum Byte {
    A,
    B,
    C,
    D
}

impl ops::Index<Byte> for Address {
    type Output = u8;

    fn index<'a>(&'a self, byte: Byte) -> &'a Self::Output {
        match byte {
            Byte::A => ((self.0 & 0xFF000000) >> 24) as u8,
            Byte::B => ((self.0 & 0x00FF0000) >> 16) as u8,
            Byte::C => ((self.0 & 0x0000FF00) >> 8) as u8,
            Byte::D => (self.0 & 0x000000FF) as u8,
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这显然不编译,因为我不能返回u8&u8的预期.天真的尝试解决它将是:

impl ops::Index<Byte> for Address {
    type Output = u8;

    fn index<'a>(&'a self, byte: Byte) -> &'a Self::Output {
        match byte {
            Byte::A => &(((self.0 & 0xFF000000) >> 24) as u8),
            Byte::B => &(((self.0 & 0x00FF0000) >> 16) as u8),
            Byte::C => &(((self.0 & 0x0000FF00) >> 8) as u8),
            Byte::D => &((self.0 & 0x000000FF) as u8),
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,当然,一旦函数返回,我就不能返回对不存在的值的引用.

我有办法std::ops::Index在这种情况下实施吗?对我来说似乎不是这样,但我希望有人可以证明我错了.

DK.*_*DK. 6

好吧,解决这个问题的最简单和最惯用的方法是实现Index,而只是使用一个名为octet或者某种方法的方法. Index用于索引容器; 它只是与动态生成新值不兼容.

所以.有你的答案.


你绝对应该没有做任何事情,我来形容,因为没有很好的理由这样做,而我只是写出来,因为你在技术上询问是否有任何方式在所有...

你被警告过了.

......八位字节就在那里!除非您正在编译一个字节不是 8位的机器,或者具有比8位更精细的寻址,否则你没有理由不这样做:

use std::ops;

#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Address(u32);

enum Byte {
    A,
    B,
    C,
    D
}

impl ops::Index<Byte> for Address {
    type Output = u8;

    #[cfg(target_endian="big")]
    fn index<'a>(&'a self, byte: Byte) -> &'a u8 {
        use std::mem;
        let bytes = unsafe { mem::transmute::<_, &[u8; 4]>(&self.0) };
        match byte {
            Byte::A => &bytes[0],
            Byte::B => &bytes[1],
            Byte::C => &bytes[2],
            Byte::D => &bytes[3],
        }
    }

    #[cfg(target_endian="little")]
    fn index<'a>(&'a self, byte: Byte) -> &'a u8 {
        use std::mem;
        let bytes = unsafe { mem::transmute::<_, &[u8; 4]>(&self.0) };
        match byte {
            Byte::A => &bytes[3],
            Byte::B => &bytes[2],
            Byte::C => &bytes[1],
            Byte::D => &bytes[0],
        }
    }
}

fn main() {
    assert_eq!(Address(0x12345678)[Byte::A], 0x12);
}
Run Code Online (Sandbox Code Playgroud)

我的意思是,除了这是一个不必要的使用,unsafe为了混淆语法; 索引地址与索引整数一样有意义:非常少.