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.
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: