Reputation: 1647
I have some struct
, something like this:
struct MyStruct<T> {
field1: T,
field2: T,
field3: T,
}
What I know and really sure about the struct
:
Sized
;struct
(3
in this example);In my project it might be useful to access struct
s like this like a slices. So what I've done:
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) }
}
}
Now I can access fields of MyStruct
like this:
let s = MyStruct { field1: 1.0, field2: 2.0, field3: 3.0 };
s.as_ref()[1]
I've tested some examples both in dev
and release
modes and didn't find any errors. Still I'm not sure about this kind of pointer magic.
Rust cannot guarantee that memory layout will preserve an order of the fields. On the other hand, as far as I know, it happens only when struct
has fields with different sizes and need to be aligned.
So I'm really curious:
union
instead of this trick?impl<T> AsRef<[T]> for MyStruct<T> {
fn as_ref(&self) -> &[T] {
let tmp: &[T; 3] = unsafe { std::mem::transmute(self) };
tmp.as_ref()
}
}
Upvotes: 2
Views: 741
Reputation: 58805
- Are there any rust safe guarantee violations?
Yes, the order is not guaranteed. Even though it's unlikely to change, Rust's ABI is not stable so a future version of the Rust compiler could do things differently.
You can force the C ABI to be used instead, which will make it safe:
#[repr(C)]
struct MyStruct<T> {
field1: T,
field2: T,
field3: T,
}
A possibly better approach, avoiding unsafe
, would be to store the fields in an array and then provide accessors instead:
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?
These are likely to compile to the same thing. If you aren't sure then test. For simple things you can pase code into rust.godbolt.org and compare the assembly.
- Should I use Union instead of this trick?
That would also only be safe with #[repr(C)]
. I can't see any obvious benefit to using a union
here, but it would mean you'd have to use even more unsafe
code, so it doesn't seem like a good idea.
Upvotes: 3