Is it safe to make a slice from struct elements?

Hi, I have a simple question, if there's a struct with two or more consecutive elements of the same type, is it safe to create a slice from them?

Example:

pub struct Holder {
    x: i32,
    y: i32,
    z: i32
}

pub fn slice_yz(h: &Holder) -> &[i32] {
    unsafe { std::slice::from_raw_parts(&h.y, 2) }
}

By default no, as for a repr(Rust) struct there's no guarantee on field order nor lack of padding -- so the compiler might have put y at the end of the struct, making that unsound.

You could, however, apply #[repr(C)] to the type to get more guarantees, in which case it becomes possible to do this soundly.

6 Likes

Note that even with #[repr(C)] it still doesn't become sound the way it's done above using &h.y. That reference must not be used to access anything other than the field y.

It's also possible in this example to use existing crates to avoid using unsafe (and thus potentially unsound) code. E. g. bytemuck - Rust

use bytemuck::{cast_ref, Pod, Zeroable};

#[repr(C)]
#[derive(Pod, Copy, Clone, Zeroable)]
pub struct Holder {
    x: i32,
    y: i32,
    z: i32,
}

pub fn slice_yz(h: &Holder) -> &[i32] {
    &cast_ref::<_, [i32; 3]>(h)[1..]
}

(playground)

5 Likes

I'm guessing what you are suggesting here is that we should instead be using slice::from_raw_parts(&h, 3) then use [1..] to get the last two elements?

That way we'll only be accessing something inside the memory pointed to by &h and not accessing anything outside the &h.y allocation (assuming #[repr(C)] and so on).

I was actually suggesting to avoid usage of unsafe whenever possible.

On a secondary note, yes, that approach would be sound, except you'll have to cast the *const Holder to *const i32 first. While you're at it, it might be easier to just cast *const Holder to *const [i32; 3] directly.

4 Likes

That's a very good point. One would need to use the raw pointer macros, like

std::slice::from_raw_parts(std::ptr::addr_of!(h.y), 2)

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.