Home > Software engineering >  How to convert pointer to multidimensional slice/array
How to convert pointer to multidimensional slice/array

Time:11-16

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!

CodePudding user response:

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);
}
  • Related