将 `struct` 投射到 rust 中是否安全?

Is it safe to cast `struct` to a slice in rust?

我有一些 struct,像这样:

struct MyStruct<T> {
    field1: T,
    field2: T,
    field3: T,
}

我对 struct 的了解和确定:

在我的项目中,像切片一样访问 struct 可能很有用。所以我做了什么:

impl<T> AsRef<[T]> for MyStruct<T> {
    fn as_ref(&self) -> &[T] {
        let ptr = self as *const Self;
        let ptr2 = ptr as *const T;
        unsafe { std::slice::from_raw_parts(ptr2, 3) }
    }
}

现在我可以像这样访问 MyStruct 的字段:

let s = MyStruct { field1: 1.0, field2: 2.0, field3: 3.0 };
s.as_ref()[1]

我已经在 devrelease 模式下测试了一些示例,但没有发现任何错误。我仍然不确定这种指针魔术。

Rust 不能保证内存布局会保留字段的顺序。另一方面,据我所知,它仅在 struct 具有不同大小的字段并且需要对齐时才会发生。

所以我很好奇:

  1. 是否存在防锈安全保障违规行为?
  2. 此变体与下一个变体之间是否存在任何安全或性能差异?
  3. 我应该使用 union 而不是这个技巧吗?
impl<T> AsRef<[T]> for MyStruct<T> {
    fn as_ref(&self) -> &[T] {
        let tmp: &[T; 3] = unsafe { std::mem::transmute(self) };
        tmp.as_ref()
    }
}
  1. Are there any rust safe guarantee violations?

是的,不能保证顺序。尽管不太可能改变,但 Rust 的 ABI 并不稳定,因此未来版本的 Rust 编译器可能会做不同的事情。

您可以强制改用 C ABI,这样会更安全:

#[repr(C)]
struct MyStruct<T> {
    field1: T,
    field2: T,
    field3: T,
}

避免 unsafe 的一种可能更好的方法是将字段存储在数组中,然后提供访问器:

struct MyStruct<T> {
    fields: [T; 3],
}

impl<T> MyStruct<T> {
    fn field1(&self) -> &T {
        &self[0]
    }

    fn field1_mut(&mut self) -> &mut T {
        &mut self[0]
    }

    // etc, you could generate these with a macro if there are a lot
}
  1. Is there any safe or performance difference between this variant and the next one?

这些很可能编译成同样的东西。如果您不确定,请进行测试。对于简单的事情,您可以将代码插入 rust.godbolt.org 并比较程序集。

  1. Should I use Union instead of this trick?

这也只有 #[repr(C)] 才安全。我看不出在这里使用 union 有什么明显的好处,但这意味着你必须使用 甚至更多 unsafe 代码,所以它不会看起来是个好主意。