【问题标题】:Transmuting u8 buffer to struct in Rust在 Rust 中将 u8 缓冲区转换为结构
【发布时间】:2017-02-28 02:23:27
【问题描述】:

我有一个未知大小的字节缓冲区,我想创建一个指向缓冲区开头内存的局部结构变量。按照我在 C 中所做的,我在 Rust 中尝试了很多不同的东西,并且不断出错。这是我最近的尝试:

use std::mem::{size_of, transmute};

#[repr(C, packed)]
struct MyStruct {
    foo: u16,
    bar: u8,
}

fn main() {
    let v: Vec<u8> = vec![1, 2, 3];
    let buffer = v.as_slice();
    let s: MyStruct = unsafe { transmute(buffer[..size_of::<MyStruct>()]) };
}

我收到一个错误:

error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
   --> src/main.rs:12:42
    |
12  |     let s: MyStruct = unsafe { transmute(buffer[..size_of::<MyStruct>()]) };
    |                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
    |
    = help: the trait `std::marker::Sized` is not implemented for `[u8]`
    = note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>

【问题讨论】:

  • 您将无法执行此操作,因为需要transmute 在编译时知道大小。您使用*mut 指针的解决方案看起来就像您必须这样做

标签: data-structures rust


【解决方案1】:

您可以使用raw pointers 中的方法和std::ptr 中的函数直接就地读/写对象。

在你的情况下:

fn main() {
    let v: Vec<u8> = vec![1, 2, 3];
    let s: MyStruct = unsafe { std::ptr::read(v.as_ptr() as *const _) };
    println!("here is the struct: {:?}", s);
}

我鼓励您将其包装在可重用的方法中,并在尝试读取之前对源缓冲区执行长度检查。

【讨论】:

  • 谢谢,不知道std::ptr中的读写功能。我在std::mem 中寻找类似的东西。有点奇怪的 IMO,那些是单独的模块。
  • @sudo:我同意我也不清楚这个部门:(
【解决方案2】:

如果您不想将数据复制到结构中而是将其保留在原处,您可以使用slice::align_to。这会创建一个 &amp;MyStruct 来代替:

#[repr(C, packed)]
#[derive(Debug, Copy, Clone)]
struct MyStruct {
    foo: u16,
    bar: u8,
}

fn main() {
    let v = vec![1u8, 2, 3];

    // I copied this code from Stack Overflow
    // without understanding why this case is safe.
    let (head, body, _tail) = unsafe { v.align_to::<MyStruct>() };
    assert!(head.is_empty(), "Data was not aligned");
    let my_struct = &body[0];

    println!("{:?}", my_struct);
}

在这里,使用align_to 将一些字节转换为MyStruct 是安全的,因为我们使用了repr(C, packed),并且MyStruct 中的所有类型都可以是任意字节。

另见:

【讨论】:

  • 很好,我想这更适合这个问题。我不记得我在 2017 年试图用这个做什么,关于与 asn1c 代码的接口。
  • @sudo align_to 在 2017 年不存在,所以这个答案的等价物会更加丑陋......
  • 嘿。我当时也在使用过时的 Rust 版本。
【解决方案3】:

我放弃了转化的东西。 Rust 中的*mut(原始指针)与 C 指针非常相似,所以这很简单:

#[repr(C, packed)] // necessary
#[derive(Debug, Copy, Clone)] // not necessary
struct MyStruct {
    foo: u16,
    bar: u8,
}

fn main() {
    let v: Vec<u8> = vec![1, 2, 3];
    let buffer = v.as_slice();
    let mut s_safe: Option<&MyStruct> = None;
    let c_buf = buffer.as_ptr();
    let s = c_buf as *mut MyStruct;
    unsafe {
        let ref s2 = *s;
        s_safe = Some(s2);
    }
    println!("here is the struct: {:?}", s_safe.unwrap());
}

unsafe 标签可不是开玩笑的,但我使用它的方式,我知道我的缓冲区已满,并在稍后采取涉及字节序的适当预防措施。

【讨论】:

  • 您不需要 unsafe 块内的前两行。最好将它们移出以尽量减少潜在不安全代码的数量。此外,鉴于您不知道缓冲区的大小,您需要确保它足够大,否则可能会出现恐慌。 Here is an example.
  • 谢谢,这个例子比我的好。我忘了利用 Rust 对向量大小的了解来让我的更安全。
猜你喜欢
  • 2021-03-13
  • 2018-04-19
  • 1970-01-01
  • 1970-01-01
  • 2016-10-28
  • 2023-03-02
  • 2021-01-14
  • 1970-01-01
相关资源
最近更新 更多