Mapping arrays to structs

This new problem is relevant to my topic on reading structs from raw memory. I was able to solve that problem, however I'm now encountering a new problem, which is why I thought I'd post it in a new topic. These lines of my code:

let cmdfis = unsafe {
let raw_ptr = cmdtbl.cfis as *mut FisRegH2D;
raw_ptr.as_mut().unwrap() as &mut FisRegH2D

Fails with error E0605. cfis is:

pub cfis: [cty::c_uchar; 64usize],

I want it to be converted into this:

#[derive(Debug, Default, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)]
pub struct FisRegH2D {
    pub fis_type: cty::c_uchar,
    _bitfield_1: internal::bitfield<[u8; 1usize], u8>,
    pub command: cty::c_uchar,
    pub feature_lo: cty::c_uchar,
    pub lba0: cty::c_uchar,
    pub lba1: cty::c_uchar,
    pub lba2: cty::c_uchar,
    pub device: cty::c_uchar,
    pub lba3: cty::c_uchar,
    pub lba4: cty::c_uchar,
    pub lba5: cty::c_uchar,
    pub feature_hi: cty::c_uchar,
    pub count_lo: cty::c_uchar,
    pub count_hi: cty::c_uchar,
    pub icc: cty::c_uchar,
    pub control: cty::c_uchar,
    rsv1: [cty::c_uchar; 4usize],

I would use slice_from_raw_parts_mut and/or slice_from_raw_parts, but I don't think that would work (after the pointer retrieval is done, I need to use the returned reference as a struct). Rustc recommends I implement a type conversion, but I have absolutely no idea how I would even do that (i.e. which elements of the array would map to which struct members). or reference, the error I get is this:

error[E0605]: non-primitive cast: `[u8; 64]` as `*mut drivers::storage::ahci::FisRegH2D`
    --> src\drivers\storage\
1227 | let raw_ptr = cmdtbl.cfis as *mut FisRegH2D;
     |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait

Issue is that I don't know how I could apply the from trait to work this way. In C, I know that I can just declare an array, typecast it into a structure and then access it as though it had been a struct all along. I like rust though and so am wondering if there's a way to do something like this (but maybe in a safer way)?

You're trying to convert an [u8; 64] to a pointer. One is 64 bytes and the other is eight. I'm assuming you want to convert a pointer to a [u8; 64] to a pointer. In order to cast between pointer types you can do this:

let ptr = &mut cmdtbl.cfis;
let fis_reg_h2d = unsafe { &mut *(ptr as *mut [u8; 64] as *mut FisRegH2D) };

Note that this turns it back into a mutable reference after the cast.

One important thing you have to consider is that if you later create a reference to the cmdtbl, then you have a reference that aliases with the one we just created above and this is undefined behaviour if either reference is mutable. Note that calling a function on cmdtbl involves creating such a reference.

If you need to touch cmdtbl while this reference exists, it's probably best to copy it out and let the reference disappear.

Marked as solution. Thank you for that. I'll keep that in mind (I doubt this'll be the last time I do something like this).

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.