3

I am working on a project involving Rust and WebAssembly using the "wasm32-unknown-unknown" target. It is no problem to return a Vec<i32> from my Rust code using this function:

#[no_mangle]
pub extern "C" fn calc_vector() -> usize {
    unsafe {
        vec_len = 0;
    }

    let mut data: Vec<i32> = Vec::new();

    for i in 0..1000 {
        data.push(i);
    }

    unsafe {
        vec_len = data.len();
    }

    data.as_mut_ptr() as usize
}

This returns an offset and I call another function from JS to get the length of my Vec. I then build the Vector in JavaScript again (knowing that i32 -> 4x uint8):

let vec_addr = exports.calc_vector();
let vec_len = exports.get_vec_len();

while(arr.length < vec_len) {
    let numberUint8 = new DataView(view.buffer, vec_addr, 4);
    let number = numberUint8.getInt32(0, true);
    arr.push(number)

    // move to next value in vector
    vec_addr += 4;
}

Using this, I want to create a Rust function that returns a Vec<Vec<i32>>, however, it just won't work:

#[no_mangle]
pub extern "C" fn calc_vector_in_vector() -> usize {
    unsafe {
        vec_len = 0;
        elements_in_vect = 0;
    }

    let mut outer_vec: Vec<*mut i32> = Vec::new();
    let mut inner_vec: Vec<i32> = Vec::new();

    for i in 0..100 {
        inner_vec.push(i);
        unsafe {
            elements_in_vect += 1;
        }
    }

    outer_vec.push(inner_vec.as_mut_ptr());

    unsafe {
        vec_len = outer_vec.len();
    }

    outer_vec.as_mut_ptr() as usize
}

I thought that I could use the same logic as with a single Vec: at the address returned from calc_vector_in_vector() is the first entry of the outer vector, containing the address as i32 of the first element of the inner vector (an actual number). However, on this address there seems to be nonsense. What I am doing wrong here?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Johnni O.
  • 121
  • 8
  • 3
    I'm not sure how the first example works. You are returning a pointer to a value which immediately goes out of scope. I haven't used wasm, but I guess the way to do this is to allocate a buffer in Javascript, and fill the buffer from Rust. That way, Javascript owns the data and it won't go out of scope. – Peter Hall Sep 16 '18 at 11:54
  • 1
    Please see [How to return a string (or similar) from Rust in WebAssembly?](https://stackoverflow.com/q/47529643/155423) to learn how to correctly return one `Vec`. – Shepmaster Sep 16 '18 at 12:31

1 Answers1

1

Because the vectors in my vector are all the same length, I can use the lazy_static crate to initialize a static Vec wrapped in a Mutex (to be able to alter it later on). LEN_VEC is the length of the vector and SIZE_INNER_VEC the size of one vector in my static vector.

I then add i32 to the static vector and return the address of the Vec using ARRAY.lock().unwrap().as_mut_ptr() as usize. With SIZE_INNER_VEC and LEN_VEC, I can recreate the vectors in my vector in JavaScript.

static mut LEN_VEC: usize = 0;
static mut SIZE_INNER_VEC: usize = 0;

lazy_static! {
    // Wrap in Mutex to change later on
    static ref ARRAY: Mutex<Vec<i32>> = Mutex::new(vec![]);
}

pub fn vector_in_vector() -> usize {
    //set LEN_VEC
    //set SIZE_INNER_VEC

    ARRAY.lock().unwrap().as_mut_ptr() as usize
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Johnni O.
  • 121
  • 8