Rust
Callback
Pass callbacks from C into Rust.
Callbacks
Some C functions take a function pointer as a parameter. On the Rust side, that pointer's type is extern "C" fn(..) -> .., and the function it points to must be defined with the C calling convention. The classic example is C's qsort:
use std::ffi::{c_int, c_void};
unsafe extern "C" {
fn qsort(
taban: *mut c_void,
adet: usize,
boyut: usize,
karsilastir: extern "C" fn(*const c_void, *const c_void) -> c_int,
);
}
The comparison function
The callback converts the void pointers it receives into the type it knows and returns negative, zero or positive per the C contract:
extern "C" fn kucukten_buyuge(a: *const c_void, b: *const c_void) -> c_int {
// SAFETY: qsort only passes addresses of the array's i32 elements.
let (x, y) = unsafe { (*(a as *const i32), *(b as *const i32)) };
x.cmp(&y) as c_int
}
The discriminants of Ordering are -1, 0 and 1; that's why as c_int directly gives the value C expects.
Exercise Sort with qsort
Sort the array let mut v = [5, 2, 9, 1]; with the qsort declaration and the kucukten_buyuge callback above (v.as_mut_ptr() as *mut c_void, v.len(), size_of::<i32>()) and print it with {:?}.
Show example solution
use std::ffi::{c_int, c_void};
unsafe extern "C" {
fn qsort(
taban: *mut c_void,
adet: usize,
boyut: usize,
karsilastir: extern "C" fn(*const c_void, *const c_void) -> c_int,
);
}
extern "C" fn kucukten_buyuge(a: *const c_void, b: *const c_void) -> c_int {
// SAFETY: qsort yalnızca dizideki i32 elemanlarının adreslerini verir.
let (x, y) = unsafe { (*(a as *const i32), *(b as *const i32)) };
x.cmp(&y) as c_int
}
fn main() {
let mut v = [5, 2, 9, 1];
// SAFETY: işaretçi, adet ve boyut v ile tutarlıdır.
unsafe {
qsort(v.as_mut_ptr() as *mut c_void, v.len(), std::mem::size_of::<i32>(), kucukten_buyuge);
}
println!("{:?}", v);
}
Test yourself
-
What happens if a panic occurs inside a callback?
- It's ignored
- It returns to Rust
- It can't cross the C boundary; the program is aborted
- An error returns to C
Show answer
It can't cross the C boundary; the program is aborted — Unwinding a panic through C code from an extern "C" function used to be undefined; it's now aborted at the boundary. catch_unwind can be used in the callback.
-
C calling convention
____ fn kucukten_buyuge(a: *const c_void, b: *const c_void) -> c_int {
0
}
Show answer
extern "C" fn kucukten_buyuge(a: *const c_void, b: *const c_void) -> c_int {
0
}
-
Item and meaning
- extern "C" fn(..)
- C function pointer
- *const c_void
- Untyped pointer
- x.cmp(&y) as c_int
- -1, 0, 1
- size_of::<i32>()
- Element size
Next lesson: Drop and cleanup