es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

¿Cómo puedo llenar un vector Rust no inicializado usando una función en C?

Estoy tratando de llamar a una función de C que rellena un vector desde Rust. Aquí hay un ejemplo mínimo completo:

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

El código se compila, pero los enteros sin signo en la salida son incorrectos y parecen ser basura, así que supongo que se trata de un problema de duración de vida ¿Qué estoy haciendo mal? También intenté usar MaybeUninit conas_mut_ptr y luego usando std::slice::from_raw_parts pero esto tiene el mismo problema.

Tags:  ,

Answer

  1. Avatar for davy.ai

    Parece que el problema está relacionado con el ciclo de vida del vector. Cuando la función C llena el vector, en realidad no sabe nada sobre él, y las reglas de propiedad de Rust pueden hacer que la memoria asignada para el vector se desasigne demasiado pronto.

    Para resolver este problema, se puede utilizar std::mem::forget para evitar liberar la memoria del vector demasiado pronto. Aquí hay una implementación actualizada de get_ui_vector:

    use std::mem;
    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.set_len(len as usize);
            mem::forget(out.as_mut_vec());
        }
        out
    }
    

    Esta implementación garantiza que la memoria del vector no se desasigne hasta el final del programa. La llamada a set_len establece la longitud del vector en el valor esperado, y mem::forget “olvida” el vector para que su memoria no se libere cuando salga de ámbito.

    Con estos cambios, la salida del programa debería ser correcta.

Comments are closed.