ProgrammazioneSviluppatore Backend

Qual è la differenza tra l'uso di move e borrow quando si passano variabili alle funzioni in Rust?

Supera i colloqui con l'assistente IA Hintsage

Risposta

Quando una variabile viene passata a una funzione, può essere passata per riferimento (borrow, utilizzando & o &mut), oppure può essere spostata (move, senza riferimento).

Borrow: viene passato un riferimento ai dati. I dati rimangono accessibili dopo la chiamata della funzione, ma per un riferimento immutabile non è possibile modificare il contenuto, mentre per un riferimento mutabile può esserci solo 1 riferimento attivo.

fn read_length(s: &String) -> usize { s.len() }

Move: la variabile viene "trasferita" interamente nella funzione. Dopo il passaggio non è possibile utilizzare la variabile originale — è stata spostata e qualsiasi tentativo di accesso genererà un errore di compilazione.

fn destroy(s: String) { println!("{}", s); } // s verrà distrutta all'uscita let s = String::from("world"); destroy(s); // s non può più essere utilizzata

Questo previene la doppia liberazione della memoria e altri errori di possesso.

Domanda trabocchetto

Posso usare la variabile dopo che è stata passata alla funzione per valore (move)?

No! Dopo che la variabile è stata passata per valore — ad esempio, String — la variabile originale diventa non valida:

let s = String::from("abc"); consume(s); // s non è più valida qui println!("{}", s); // errore di compilazione

Molti confondono questo comportamento con quello dei tipi con Copy (ad esempio, i32), dove la variabile rimane valida dopo il passaggio.

Esempi di errori reali a causa della conoscenza superficiale dell'argomento


Storia

Un giovane programmatore ha scritto una funzione di elaborazione delle stringhe che accettava String e non &String. Di conseguenza, la stringa originale diventava non accessibile dopo la chiamata della funzione, portando a un caricamento doppio e a un'allocazione di memoria aggiuntiva nel server di registrazione.


Storia

In un microservizio, una risorsa critica veniva passata a diversi thread tramite move, dopo di che i tentativi di accesso a questa risorsa nel thread principale portavano a errori di compilazione. È stato necessario rifattorizzare urgentemente l'architettura affinché la risorsa fosse passata per riferimento o tramite wrapper come Arc.


Storia

Nel parser eventi interno, durante l'elaborazione dei dati, la variabile veniva rapidamente spostata (move) in una closure, dopo di che gli accessi ad essa al di fuori della closure generavano errori di compilazione. Il problema è stato notato solo durante la revisione — è stato introdotto uno stile di utilizzo obbligatorio di borrow per i dati che devono vivere più a lungo della funzione locale.