ProgrammazioneSviluppatore di sistema

Spiega come funziona il system FFI (Foreign Function Interface) in Rust. Quali sono i requisiti e le insidie per una chiamata sicura a funzioni in C?

Supera i colloqui con l'assistente IA Hintsage

Risposta

FFI in Rust consente di chiamare funzioni dichiarate in librerie esterne (ad esempio, C/C++) ed esportare funzioni Rust. Per questo si utilizza la parola chiave extern. Requisiti:

  • Tutte le chiamate FFI devono essere racchiuse in una dichiarazione con unsafe;
  • L'ABI (Application Binary Interface) deve corrispondere (di solito "C");
  • I tipi di dati devono essere compatibili — è importante utilizzare primitivi di dimensioni fisse (i32, u64, ecc.);
  • Gestione della memoria — prestare molta attenzione alla proprietà, perdite e doppia liberazione.

Esempio di wrapping:

extern "C" { fn abs(input: i32) -> i32; } fn main() { unsafe { println!("{}", abs(-5)); } }

Esportare una funzione da Rust per l'uso in C si può fare così:

#[no_mangle] pub extern "C" fn sum(a: i32, b: i32) -> i32 { a + b }

Domanda trabocchetto

Domanda: Garantisce Rust che se si racchiude una chiamata a una funzione C in unsafe, tutto funzionerà correttamente in termini di sicurezza dei thread e cattura di UB?

Risposta: No! unsafe è una promessa al compilatore che ci si assume la responsabilità di tutte le esigenze di sicurezza (aliasing, sicurezza dei thread, accesso alla memoria). Rust non esegue controlli sul codice all'interno di C. Ad esempio, una condizione di competizione o UB nel codice della libreria potrebbe "rompere" il runtime del programma Rust. Anche se il codice Rust viene compilato, l'esecuzione reale potrebbe portare a crash.


Storia

In un fornitore di dati finanziari, il team ha integrato una libreria C tramite FFI senza controllare le dimensioni dei tipi. Su x86_64 ci si aspettava che long e i64 coincidessero, ma si è scoperto che le dimensioni non coincidevano su altre piattaforme — una lettura della memoria errata ha portato a bug nei calcoli.

Storia

Un sviluppatore ha creato un wrapper per l'API C, dimenticando i puntatori passati ai buffer. Poiché Rust liberava automaticamente la memoria, la funzione C a volte operava con memoria già liberata, portando a crash e aborti.

Storia

In un prodotto client-server, un modulo Rust accedeva a una libreria C, senza considerare la sicurezza multi-thread. La libreria non era thread-safe e i programmi Rust vi accedevano contemporaneamente da diversi thread, il che ha portato a crash e corruzione dei dati.