Callback
- Rust course
- Advanced
- Lesson 39/81
- 4 min
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.
Practice 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 {:?}.
Starter code
use std::ffi::{c_int, c_void};
fn main() {
// buraya yaz
}Sample 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);
}Expected output
[1, 2, 5, 9]
Questions and answers
Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.
1 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 Correct answer
- An error returns to C
Explanation: 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.
2 C calling convention
____ fn kucukten_buyuge(a: *const c_void, b: *const c_void) -> c_int {
0
}
extern "C" fn kucukten_buyuge(a: *const c_void, b: *const c_void) -> c_int {
0
}
Explanation: A callback given to C is defined with the C calling convention.
3 Item and meaning
| Term | 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 |