将 `struct` 投射到 rust 中是否安全?
Is it safe to cast `struct` to a slice in rust?
我有一些 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]
我已经在 dev
和 release
模式下测试了一些示例,但没有发现任何错误。我仍然不确定这种指针魔术。
Rust 不能保证内存布局会保留字段的顺序。另一方面,据我所知,它仅在 struct
具有不同大小的字段并且需要对齐时才会发生。
所以我很好奇:
- 是否存在防锈安全保障违规行为?
- 此变体与下一个变体之间是否存在任何安全或性能差异?
- 我应该使用
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()
}
}
- 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
}
- Is there any safe or performance difference between this variant and the next one?
这些很可能编译成同样的东西。如果您不确定,请进行测试。对于简单的事情,您可以将代码插入 rust.godbolt.org 并比较程序集。
- Should I use Union instead of this trick?
这也只有 #[repr(C)]
才安全。我看不出在这里使用 union
有什么明显的好处,但这意味着你必须使用 甚至更多 unsafe
代码,所以它不会看起来是个好主意。
我有一些 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]
我已经在 dev
和 release
模式下测试了一些示例,但没有发现任何错误。我仍然不确定这种指针魔术。
Rust 不能保证内存布局会保留字段的顺序。另一方面,据我所知,它仅在 struct
具有不同大小的字段并且需要对齐时才会发生。
所以我很好奇:
- 是否存在防锈安全保障违规行为?
- 此变体与下一个变体之间是否存在任何安全或性能差异?
- 我应该使用
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()
}
}
- 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
}
- Is there any safe or performance difference between this variant and the next one?
这些很可能编译成同样的东西。如果您不确定,请进行测试。对于简单的事情,您可以将代码插入 rust.godbolt.org 并比较程序集。
- Should I use Union instead of this trick?
这也只有 #[repr(C)]
才安全。我看不出在这里使用 union
有什么明显的好处,但这意味着你必须使用 甚至更多 unsafe
代码,所以它不会看起来是个好主意。