pointersrustrust-bindgen

How to convert pointer to multidimensional slice/array


I'm writing a c-binding to a Rust function. The Rust function takes a 3D slice where 2 dimensions are of size two. Essentially it's a slice of 2D line segments where a line segment is represented by 2 points.

This means the segments have type:

segments: [[[f32; 2]; 2]]

Now since I call this from C I only have a simple f32 pointer at the FFI boundary. My multi-dimensional array from c is in row-major memory order which I understand matches what Rust would expect. So morally I should be able to say to rust: It's just that type.

I have looked at https://doc.rust-lang.org/std/ptr/fn.slice_from_raw_parts.html but I don't see how I can handle a more complex structure with that.

So to make it very concrete I want to be able to call foo from foo_c and foo should handle the conversion from pointer to the 3D slice/array structure.

#[no_mangle]
pub unsafe extern fn foo_c(segments: *f32, n_segments: usize) {

    foo(...)
}

fn foo(segments: [[[f32; 2]; 2]]) {

    ...
}

If possible I would like to do this without copying any data around.

Any help is appreciated!


Solution

  • First I think you made some typos, so I'm assuming your code is:

    #[no_mangle]
    // missing `const`
    pub unsafe extern fn foo_c(segments: *const f32, n_segments: usize) {
        foo(...)
    }
    // missing `&`
    fn foo(segments: &[[[f32; 2]; 2]]) {
        ...
    }
    

    The solution is:

    #[no_mangle]
    pub unsafe extern fn foo_c(segments: *const f32,n_segments: usize) {
    
        // first we cast the pointer to get the slice `T`
        // so from a generic `*const f32` to `*const T` where T is `[[f32; 2]; 2]`
        let ptr = segments as *const [[f32; 2]; 2];
    
        // we construct the slice using `slice_from_raw_parts` 
        // after we got the correct pointer.
        let segments_slice = std::ptr::slice_from_raw_parts::<[[f32;2];2]>(ptr,n_segments);
    
        // we still have `*const [[f32; 2]; 2]` which we need to convert
        // to `&[[f32; 2]; 2]` so we use `&*` (dereference then borrow).
        foo(&*segments_slice)
    }
    
    fn foo(segments: &[[[f32; 2]; 2]]) {
        println!("segments {:?}",segments);
    }