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

Rust 当输出值不是类型的一部分时,如何实现std::ops::Index,rust,operator-overloading,lifetime,traits,Rust,Operator Overloading,Lifetime,Traits,我正在尝试实现一种ip地址类型,它基本上包装了u32: #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Address(u32); 我正在实现对IP地址有意义的std::ops操作符(&,|,+,-,等等)。唯一引起麻烦的是std::ops::Index: #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct

我正在尝试实现一种ip地址类型,它基本上包装了
u32

#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Address(u32);
我正在实现对IP地址有意义的
std::ops
操作符(
&
|
+
-
,等等)。唯一引起麻烦的是
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,
        }
    }
}
但是,当然,我不能返回对一个一旦函数返回就不存在的值的引用


在这种情况下,有没有办法实现
std::ops::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);
}
使用std::ops;
#[派生(复制、克隆、PartialEq、Eq、PartialOrd、Ord、散列)]
发布结构地址(u32);
枚举字节{
A.
B
C
D
}
impl ops::地址的索引{
类型输出=u8;
#[cfg(target_endian=“big”)]
fn索引和'a u8{
使用std::mem;
let bytes=unsafe{mem::transmute::(&self.0)};
匹配字节{
字节::A=>&字节[0],
字节::B=>&字节[1],
字节::C=>&字节[2],
字节::D=>&字节[3],
}
}
#[cfg(target_endian=“little”)]
fn索引和'a u8{
使用std::mem;
let bytes=unsafe{mem::transmute::(&self.0)};
匹配字节{
字节::A=>&字节[3],
字节::B=>&字节[2],
字节::C=>&字节[1],
字节::D=>&字节[0],
}
}
}
fn main(){
断言(地址(0x12345678)[字节::A],0x12];
}

我的意思是,除了为了混淆语法而不必要地使用
不安全的
;为地址编制索引和为整数编制索引一样有意义:很少。

非常感谢您解释为什么这样做是错误的,并回答了这个问题。我保证我不会实现
索引
!冒着丢失某些明显内容的风险,已经存在一个封装适当底层类型的方法。@Shepmaster,是的,但我想在流量生成工具中使用它,因此我希望(至少)实现
+
&
操作符。恐怕我无法在
Ipv4Addr
上轻松实现这一点,因为我无法直接访问底层类型。
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);
}