0

I'm trying to call a C function that fills in a vector from Rust. Here is a complete minimal working example:

Cargo.toml

[package]
name = "so"
version = "0.1.0"
edition = "2021"

[build-dependencies]
cc = "1.0.72"

build.rs

fn main() {
    cc::Build::new().file("src/library.c").compile("library");
}

src/library.c

void get_ui_array(unsigned long *out, long len) {
  long i;
  for (i = 0; i < len; i++) {
    out[i] = 42;
  }
}

src/main.rs

use std::os::raw::{c_long, c_ulong};

extern "C" {
    pub fn get_ui_array(out: *mut c_ulong, len: c_long);
}

fn get_ui_vector(len: c_long) -> Vec<c_ulong> {
    let mut out = Vec::<c_ulong>::with_capacity(len as usize);
    unsafe {
        get_ui_array(out.as_mut_ptr(), len);
    }
    out
}

fn main() {
    dbg!(get_ui_vector(12));
}

The code compiles, but the unsigned integers in the output are incorrect and seem to be garbage, so I'm assuming it is a lifetime issue. What am I doing wrong? I also tried using MaybeUninit with as_mut_ptr then using std::slice::from_raw_parts but this has the same issue.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
wyoumans
  • 350
  • 1
  • 3
  • 10
  • 1
    if you doesn't use `set_len` nothing will be in the vector. Your question is very unclear – Stargateur Nov 23 '21 at 17:37
  • `with_capacity` initializes an empty vecot of length `len`, I'm not sure why `set_len` would be needed. Please tell me what isn't clear about the question @Stargateur – wyoumans Nov 23 '21 at 17:49
  • @wyoumans did you put anything into the vector? The values being garbage is to be expected. Also instead of `set_len`, it is safer if you can initialize it to some default value (`let mut out = vec![0; len as usize];`) and let the compiler determine it can be uninitialized. However, this might not be ideal if linking with a shared library. – Locke Nov 23 '21 at 17:50
  • The C function should be assigning to the vector. Assuming that works as expected why would the vector have garbage? @Locke – wyoumans Nov 23 '21 at 17:53
  • @shepmaster thanks for making an example. I assumed `with_capacity` was setting the length (im still not sure why it doesn't). Ill try your suggestion – wyoumans Nov 23 '21 at 19:05
  • Also `len` is guaranteed > 0 here. – wyoumans Nov 23 '21 at 19:06
  • *im still not sure why it doesn't* — when you set the capacity, you haven't set any values. If the values are unset, what would happen when you print them? It sounds like you would like to be able to do `dbg!(Vec::::with_capacity(1))`, but since that `String` hasn't been initialized, it would cause undefined behavior. Why would you wish for such a terrible thing to happen to your code? – Shepmaster Nov 23 '21 at 19:12
  • @shepmaster oh duh, for some reason I was thinking it would be initialized/set to zero, obviously not. I'm still confused on why the vector was not empty despite me not setting a length, ill double check that. – wyoumans Nov 23 '21 at 19:35
  • 1
    *it would be initialized/set to zero* — Zero isn't a valid value for all types. The [`NonZero*`](https://doc.rust-lang.org/stable/std/num/struct.NonZeroU8.html) family is a key example. – Shepmaster Nov 23 '21 at 19:41

1 Answers1

2

but the unsigned integers in the output are incorrect and seem to be garbage

If you don't use Vec::set_len, the length of the Vec is still zero, even though you've allocated memory and assigned values. If you print out the Vec, it will be empty, so I wonder how you are seeing any integers in the output.

That said, using set_len should fix the problem:

fn get_ui_vector(len: c_long) -> Vec<c_ulong> {
    let mut out = Vec::<c_ulong>::with_capacity(len as usize);
    unsafe {
        get_ui_array(out.as_mut_ptr(), len);
        out.set_len(len as usize); // HERE
    }
    out
}

I assumed with_capacity was setting the length (I'm still not sure why it doesn't).

When you set the capacity of the vector, you don't set any values inside the vector. If the values are unset, accessing them would cause undefined behavior.

If you wanted to define the values, you could use something like Vec::resize.

See also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • 1
    I'm not sure what I was doing to get garbage values since I can't reproduce it, but the problem is fixed now. – wyoumans Nov 23 '21 at 21:00