Ce face extern (funcții C și FFI)
Un bloc unsafe extern "C" { ... } declară funcții dintr-o bibliotecă C (FFI, Foreign Function Interface). Rust nu poate verifica ce fac acele funcții, deci apelul lor cere unsafe. În ediția 2024 blocul trebuie scris unsafe extern, iar o funcție pe care o știi sigură poate fi declarată safe fn, ca să fie apelată fără unsafe. Biblioteca C standard este legată automat; pentru altele se folosește #[link(name = "...")] sau Cargo.
Tipurile trebuie să corespundă celor din C: i32 pentru int (sau std::ffi::c_int), *const c_char pentru const char*, usize pentru size_t. Textele C se termină cu octetul 0; în Rust le scrii cu literalul c"text" (tipul &CStr) sau cu CString.
extern "C" fn definește o funcție Rust care folosește convenția de apel C, ca să poată fi dată unei biblioteci C (de exemplu o funcție de comparare pentru qsort). Cu #[unsafe(no_mangle)], numele ei rămâne neschimbat și poate fi apelată din C. extern crate este forma veche (ediția 2015) de a declara un crate dependent; azi Cargo face asta automat.
Exemple
Exemplul 1: Apelul unor funcții din biblioteca C
strlen primește un pointer la un text C, deci apelul cere unsafe. abs este declarată safe fn, deci se apelează direct. Literalul c"Iași" are octetul 0 la final; strlen numără octeții (ș are 2).
use std::ffi::{c_char, c_int};
unsafe extern "C" {
fn strlen(s: *const c_char) -> usize;
safe fn abs(x: c_int) -> c_int;
}
fn main() {
let oras = c"Iași";
// SAFETY: `oras` este un text C valid, terminat cu 0.
let lungime = unsafe { strlen(oras.as_ptr()) };
println!("strlen(\"Iași\") = {lungime} octeți");
println!("abs(-42) = {}", abs(-42));
}
Rezultat
strlen("Iași") = 5 octeți
abs(-42) = 42
Exemplul 2: O funcție Rust dată unei funcții C: qsort
qsort din C sortează orice tablou, dacă primește o funcție de comparare. compara este scrisă în Rust cu extern "C", deci C o poate apela. (În cod real ai folosi sort(); exemplul arată doar mecanismul.)
use std::ffi::{c_int, c_void};
unsafe extern "C" {
fn qsort(
baza: *mut c_void,
numar: usize,
marime: usize,
compara: extern "C" fn(*const c_void, *const c_void) -> c_int,
);
}
extern "C" fn compara(a: *const c_void, b: *const c_void) -> c_int {
// SAFETY: qsort ne dă pointeri la elemente din tabloul nostru de i32.
let (x, y) = unsafe { (*(a as *const i32), *(b as *const i32)) };
x.cmp(&y) as c_int
}
fn main() {
let mut temperaturi = [21, -3, 18, 7, 0, 25];
// SAFETY: pointerul, numărul și mărimea elementelor descriu exact tabloul.
unsafe {
qsort(
temperaturi.as_mut_ptr() as *mut c_void,
temperaturi.len(),
size_of::<i32>(),
compara,
);
}
println!("{temperaturi:?}");
}
Rezultat
[-3, 0, 7, 18, 21, 25]
Greșeli frecvente
- Scrii
extern "C" { ... }fărăunsafeîn ediția 2024: eroareextern blocks must be unsafe. - Dai unei funcții C un
&strsau unString(s.as_ptr()). Textele Rust nu se termină cu 0, deci C citește dincolo de ele. FoloseșteCString::new(text)sau un literalc"...". - Declari greșit semnătura (de exemplu
i64în loc deint). Compilatorul nu poate verifica declarațiileextern; o semnătură greșită dă comportament nedefinit la rulare. - Lași o panică să iasă dintr-o funcție
extern "C"apelată de C. Programul este oprit (abort); prinde erorile în Rust și returnează un cod de eroare.
Comenzi înrudite
unsafe și pointeri bruți: unsafe permite cinci operații pe care compilatorul nu le poate verifica, ca dereferențierea pointerilor bruți; corectitudinea o garantezi tu.fn: Declară o funcție. Tipul fiecărui parametru este obligatoriu, iar ultima expresie fără punct și virgulă este valoarea returnată.atribute (#[derive], #[cfg], #[allow]): Informații pentru compilator scrise cu #[…] deasupra unui element: generează cod (derive), aleg codul compilat (cfg) sau schimbă avertismentele.cargo add: Adaugă o dependență (un crate de pe crates.io) în Cargo.toml, cu ultima versiune compatibilă. cargo remove o scoate.