FFI passing back an array

This is my Rust fn passing back I hope a pointer to an array of f32.

#[no_mangle]
pub extern "C" fn sdrlib_disp_data() -> *mut f32 {
    let mut out_real = [0.0; (common_defs::DSP_BLK_SZ ) as usize];
    dsp::dsp_interface::wdsp_get_display_data(0, &mut out_real);
    println!("out_real[100] {}", out_real[100]);
    let v_out_real = out_real.to_vec();
    println!("v_out_real[100] {}", v_out_real[100]);
    let data = Box::new(v_out_real);
    println!("data[100] {}", data[100]);
    let r = Box::into_raw(data) as *mut _;
    unsafe {println!("r[100] {}", *((r as *mut f32).add(100)));}
    return r;
}

This is the output on the Rust side:
out_real[100] -113.57421
v_out_real[100] -113.57421
data[100] -113.57421
r[100] 1473573000000000000000000000

and on the client side
x[100] 1.473572992698795e+27

The data appears to get across the interface OK but is rubbish. Clearly I'm not understanding something about what this line does.

Box::into_raw(data) as *mut _;

This heap-allocates the array of f32s twice (you can see a Vec<f32> as a Box<[T]>).
So you end up having a Box<Vec<f32>>, so kind of like a Box<Box<[f32]>>, which as a raw pointer would be a *mut *mut f32, but you treat it as a single-layer pointer, so you are missing one layer of indirection.

In a way, you shouldn't need to as cast in your code:

-   let data = Box::new(v_out_real);
-   let r = Box::into_raw(data) as *mut _;
+   let (r, _len, _capacity) = v_out_real.into_raw_parts();

Don't forget that Vec::into_raw_parts() is still unstable. If I were writing this, I'd skip the Vec altogether and just use a boxed array:

#[no_mangle]
pub extern "C" fn sdrlib_disp_data() -> *mut f32 {
    let mut out_real = Box::new([0.0; (common_defs::DSP_BLK_SZ) as usize]);
    dsp::dsp_interface::wdsp_get_display_data(0, &mut out_real);
    println!("out_real[100] {}", out_real[100]);
    let r = Box::into_raw(out_real) as *mut f32;
    unsafe { println!("r[100] {}", *r.add(100)); }
    r
}
1 Like

Yeah, if the length is a const (which it looks to be the case), then :100:

  • Otherwise I guess we could use a Box<[f32]> and from there

    let len = boxed_slice.len();
    let ptr: *mut [f32] = Box::into_raw(boxed_slice);
    let r: *mut f32 = ptr as _;
    
1 Like

Thanks. Looks like the to_vec does mess it up even though it had the correct data in it once Boxed it was messed up. Need to check it out a bit further but the data looks correct now.

You may have been fooled by the […] indexing sugar in Rust, which is allowed to implicitly dereference:

is actually: (*v_out_real)[100],

and:

is actually (**data)[100], indirection-wise

Yes I was but the data in the client was wrong and is now correct. The fn is simplified to this.

#[no_mangle]
pub extern "C" fn sdrlib_disp_data() -> *mut f32 {
    let mut out_real = [0.0; (common_defs::DSP_BLK_SZ ) as usize];
    dsp::dsp_interface::wdsp_get_display_data(0, &mut out_real);
    return Box::into_raw(Box::new(out_real)) as *mut _;
}