微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

在 Rust 中将 `struct` 转换为切片是否安全?

如何解决在 Rust 中将 `struct` 转换为切片是否安全?

我有一些struct,像这样:

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

我对 struct 的了解和确信:

  • 所有字段的类型相同;
  • 所有字段都是Sized;
  • 每个struct(在本例中为3)的确切字段数;

在我的项目中,像切片一样访问 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. 是否存在任何违反防锈安全保证的行为?

是的,订单无法保证。尽管不太可能改变,但 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. 此变体与下一个变体之间在安全或性能方面是否存在差异?

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

  1. 我应该使用 Union 来代替这个技巧吗?

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

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。