Rust access C arrays

I'm curious if this is 'safe' in the sense its not UB.

Basically I want to provide some Arrays of C structures, which rust code can modify the data.

struct data {
        uintptr_t pointer;
        size_t stride;
        size_t count;
};

struct tform {
   float position, rotation;
}

struct data api_get_data() {
        static tform t[3] = {
                {3.1f, 4.1f},
                {3.2f, 4.2f},
                {3.3f, 4.3f},
        };

        return {
                (uintptr_t)t,
                sizeof(tform),
                3,
        };
};

I have the equivlent structure in Rust and the function prototype.

#[repr(C)]
struct Transform {
    position : f32,
    rotation : f32,
}

And I'm accessing the array like this in rust.

        let d = api_get_data();
        let bytes = d.stride * d.count;
        let ptr = d.pointer as *mut Transform;
        let mut t = std::slice::from_raw_parts_mut(ptr, bytes);

        do_thing(&mut t);

I have two questions, is this a performant solution and is this an valid solution. I saw this in the documentation for from_raw_parts which makes me think this isn't correct.

The entire memory range of this slice must be contained within a single allocated object! Slices can never span across multiple allocated objects.

The code above is not correct, and it may work but you shouldn't do this.

For one, you need to use C-compatible types in your Rust code. This code here:

Is wrong because f32 is a Rust type, but it's not C-compatible. Instead, you need to use std::os::raw::c_float.

You would probably benefit from using something like bindgen to generate bindings for C code. There is some more detail here on how to use FFI (which is what you need): FFI - The Rustonomicon

A couple things that come to mind.

Don't stopre pointers in integers. Provenance is complicated, but if something's a pointer, store it as a pointer -- as void* data; if you don't want to give it a type.

The second parameter here is the length, not the bytes, so this call as written is UB.

One static is one allocated object.

What this rule means is that you can't have one slice that covers multiple local variables, static variables, or dynamic memory allocations, even if they happen to have memory addresses next to each other.

Basically that means that if you want to make a slice, you can't do it from let mut x0: i32; let mut x1: i32;; it needs to be done as let mut x: [i32; 2];.

2 Likes