tipos - punteros y vectores en c
Cómo comprobar si el puntero de función pasado de C no es NULL (1)
Ejemplo de código a continuación
La parte de óxido:
#[no_mangle]
pub extern fn call_c_function(value: i32, fun: fn(i32) -> i32) -> i32 {
fun(value)
}
Y la parte C:
int32_t call_c_function(int32_t value, int32_t (*fun)(int32_t));
int32_t triple(int32_t x)
{
return x*3;
}
int main(int argc, char *argv[])
{
int32_t value = 3;
int32_t result = call_c_function(value, triple);
printf("%d tripled is %d/n", value, result);
call_c_function(0, NULL); // Crash here
return EXIT_SUCCESS;
}
Por supuesto, la segunda llamada de call_c_function
se bloqueará. El compilador de oxidación no se quejará sobre el código inseguro dentro de call_c_function
, porque desde el punto de vista de oxidación este código es seguro. Además, no está permitido simplemente escribir:
if !fun.is_null() {
fun(value)
}
porque el tipo de fun
es fn(i32) -> i32
(no es un puntero).
Entonces mi pregunta es, ¿cómo puedo proteger call_c_function
contra NULL puntero dereference? ¿Hay alguna manera de verificar si la devolución de llamada pasada desde C no es válida?
Tal vez tengo que cambiar la definición call_c_function
?
Puede usar la Option<...>
para representar punteros de funciones que aceptan nulos. Es incorrecto tener un valor NULL para un valor de tipo fn(...)
por lo que se requiere el contenedor de Option
para casos como este.
Por ejemplo,
#[no_mangle]
pub extern fn call_c_function(value: i32, fun: Option<fn(i32) -> i32>) -> i32 {
if let Some(f) = fun { f(value) }
}
Sin embargo, hay un punto extra: la fun
es una función C, pero el tipo fn(...)
es una función Rust. No son directamente compatibles (por ejemplo, sus convenciones de llamadas son diferentes), uno necesita usar el tipo extern "C" fn(...)
(aka extern fn(...)
) al interactuar con los punteros de función C:
#[no_mangle]
pub extern fn call_c_function(value: i32, fun: Option<extern fn(i32) -> i32>) -> i32 {
if let Some(f) = fun { f(value) }
}