问题
Consider the following:
// Just a sequence of adjacent fields of same the type
#[repr(C)]
#[derive(Debug)]
struct S<T> {
a : T,
b : T,
c : T,
d : T,
}
impl<T : Sized> S<T> {
fn new(a : T, b : T, c : T, d : T) -> Self {
Self {
a,
b,
c,
d,
}
}
// reinterpret it as an array
fn as_slice(&self) -> &[T] {
unsafe { std::slice::from_raw_parts(self as *const Self as *const T, 4) }
}
}
fn main() {
let s = S::new(1, 2, 3, 4);
let a = s.as_slice();
println!("s :: {:?}\n\
a :: {:?}", s, a);
}
- Is this code portable?
- Is it always safe to assume a repr(C) struct with fields of same type can be reinterpreted like an array? Why?
回答1:
Yes, it is safe and portable, except for very large T
(fix below). None of the points listed in the safety section of the documentation for std::slice::from_raw_parts are a concern here:
The data pointer is valid for
mem::size_of::<T>() * 4
, which is the size ofS<T>
, and is properly aligned.- All of the items are in the same allocation object, because they are in the same struct.
- The pointer is not null, because it is a cast from the safe
&self
parameter, and it is properly aligned, becauseS<T>
has (at least) the alignment ofT
.
The data parameter definitely points to 4 consecutive initialized
T
s, becauseS
is marked#[repr(C)]
, and that is the behavior of C on all platforms (repr(Rust)
makes no such guarantee).The memory referenced is not mutated during the lifetime of the reference, which is guaranteed by the borrow checker.
The total size of the slice must not be greater than
isize::MAX
. The code does not check this, so it is technically a safety hole. To be sure, add a check toas_slice
, before theunsafe
:assert!(std::mem::size_of::<S<T>>() <= isize::MAX as _);
The check will normally be optimized out.
来源:https://stackoverflow.com/questions/62240126/is-it-legal-to-cast-a-struct-to-an-array