gpt4 book ai didi

casting - Rust 中用于低级数据结构和类型转换的位域和联合

转载 作者:行者123 更新时间:2023-11-29 07:58:06 26 4
gpt4 key购买 nike

我需要管理位域数据和联合。这是我在 C 中认为的代码:

typedef struct __attribute__((__packed__)){
union {
struct __attribute__((__packed__)){
unsigned short protocol : 4;
unsigned short target : 12;
unsigned short target_mode : 4;
unsigned short source : 12;
unsigned char cmd;
unsigned char size;
};
unsigned char unmap[6]; // Unmapped form.
};
}header_t;

我使用此联合可以轻松地从映射形式切换到未映射形式。我可以写入 header_t.protocolheader_t.source 并使用 header_t.unmap 将其作为 u8 数组取回>。此开关不使用时间并共享相同的内存块。

我试图在 Rust 中做同样的事情,但我没有找到一种干净的方法来做这件事。我成功地使用了两个结构和一个专用的 impl 在它们之间切换:

#[allow(dead_code)]
pub struct Header {
protocol: u8, // 4 bits used
target: u16, // 12 bits used
target_mode: u8, // 4 bits used
source: u16, // 12 bits used
cmd: u8, // 8 bits used
size: u8, // 8 bits used
}

#[allow(dead_code)]
pub struct UnmapHeader{
tab:[u8; 6],
}

impl Header {
#[allow(dead_code)]
pub fn unmap(&self) -> UnmapHeader {
let mut unmap_header = UnmapHeader { tab: [0; 6],};
unmap_header.tab[0] = (self.protocol & 0b0000_1111) | (self.target << 4) as u8;
unmap_header.tab[1] = (self.target >> 4) as u8;
unmap_header.tab[2] = ((self.target_mode as u8) & 0b0000_1111) | (self.source << 4) as u8;
unmap_header.tab[3] = (self.source >> 4) as u8;
unmap_header.tab[4] = self.cmd;
unmap_header.tab[5] = self.size;
unmap_header
}
}

impl UnmapHeader {
#[allow(dead_code)]
pub fn map(&self) -> Header {
Header{
protocol: self.tab[0] & 0b0000_1111,
target: ((self.tab[0] & 0b1111_0000) >> 4) as u16 & (self.tab[1] << 4) as u16,
target_mode: self.tab[2] & 0b0000_1111,
source: ((self.tab[2] & 0b1111_0000) >> 4) as u16 & (self.tab[3] << 4) as u16,
cmd: self.tab[4],
size: self.tab[5],
}
}
}

#[test]
fn switch() {
let header = Header {
protocol: 0b0000_1000,
target: 0b0000_0100_0000_0001,
target_mode: 0b0000_0100,
source: 0b0000_0100_0000_0001,
cmd: 0xAA,
size: 10,
};
let unmap_header = header.unmap();
assert_eq!(unmap_header.tab[0], 0b0001_1000);
assert_eq!(unmap_header.tab[1], 0b0100_0000);
assert_eq!(unmap_header.tab[2], 0b0001_0100);
assert_eq!(unmap_header.tab[3], 0b0100_0000);
assert_eq!(unmap_header.tab[4], 0xAA);
assert_eq!(unmap_header.tab[5], 10);
}

是否有更惯用的 Rust 解决方案?

最佳答案

Rust(从最近开始)supports C-style unions .但是,联合需要一个 unsafe block ,并且如果您不必与 C 联合进行交互,则对于纯 Rust 代码来说不是惯用的。

一种方法是将基础数据建模为 [u8; 6] 然后提供更友好的accessor函数:

pub struct Header {
tab: [u8; 6],
}

impl Header {
pub fn get_protocol(&self) -> u8 {
self.tab[0] & 0b0000_1111
}

pub fn set_protocol(&mut self, value: u8) {
self.tab[0] = self.tab[0] & 0b1111_0000 | value & 0b0000_1111;
}

// etc..
}

正如您在其中一个问题评论中提到的,您可以使用 bitfield 使代码更简单。 crate 。

另一种方法是用单独的字段定义结构,但转换为 [u8; 6]。正如您所介绍的那样,这些字段占用的空间比 [u8; 6],所以没有一个方便的转换(例如不安全的 std::mem::transmute),无论如何都不必为每个单独的字段移动位。所以上面的解决方案可能更好。

无论底层表示如何,在这种情况下定义友好访问器可能是个好主意。它是一种免费的抽象,可以让您稍后改变对表示的看法,而无需更改它的使用方式。

关于casting - Rust 中用于低级数据结构和类型转换的位域和联合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46602853/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com