Shopping cart

0

Cart

  • 0 item

Nessun prodotto nel carrello.

All categories
 Corso Gratuito di Programmazione Rust Lezione 057 – Esecuzione del codice in fase di pulizia con il tratto Drop

SE VUOI PRENDERE LA CERTIFICAZIONE PER QUESTO CORSO CLICCA QUI

Corso su Come Eseguire Codice alla Pulizia con il Trait Drop

Il secondo trait importante per il pattern dei puntatori intelligenti è Drop, che ti permette di personalizzare cosa succede quando un valore sta per uscire dallo scope. Puoi fornire un’implementazione per il trait Drop su qualsiasi tipo, e quel codice può essere usato per rilasciare risorse come file o connessioni di rete.

Stiamo introducendo Drop nel contesto dei puntatori intelligenti perché la funzionalità del trait Drop è quasi sempre usata quando si implementa un puntatore intelligente. Ad esempio, quando un Box<T> viene eliminato, deallocerà lo spazio nell’heap a cui il box punta.

In alcuni linguaggi, per alcuni tipi, il programmatore deve chiamare del codice per liberare memoria o risorse ogni volta che finisce di usare un’istanza di quei tipi. Esempi includono gestori di file, socket o lock. Se dimenticano, il sistema potrebbe diventare sovraccarico e bloccarsi. In Rust, puoi specificare che un particolare pezzo di codice venga eseguito ogni volta che un valore esce dallo scope, e il compilatore inserirà questo codice automaticamente. Di conseguenza, non devi stare attento a posizionare il codice di pulizia ovunque in un programma in cui un’istanza di un particolare tipo è finita—non perderai mai risorse!

Specifichi il codice da eseguire quando un valore esce dallo scope implementando il trait Drop. Il trait Drop richiede di implementare un metodo chiamato drop che prende un riferimento mutevole a self. Per vedere quando Rust chiama drop, implementiamo drop con le istruzioni println! per ora.

rust

struct CustomSmartPointer {
data: String,
}
impl Drop for CustomSmartPointer {
fn drop(&mut self) {
println!("Rilascio CustomSmartPointer con dati `{}`!", self.data);
}
}

fn main() {
let c = CustomSmartPointer {
data: String::from("mie roba"),
};
let d = CustomSmartPointer {
data: String::from("altra roba"),
};
println!("CustomSmartPointers creati.");
}

Il trait Drop è incluso nel preludio, quindi non abbiamo bisogno di portarlo nello scope. Implementiamo il trait Drop su CustomSmartPointer e forniamo un’implementazione per il metodo drop che chiama println!. Il corpo della funzione drop è dove metteresti qualsiasi logica che desideri eseguire quando un’istanza del tuo tipo esce dallo scope. Stiamo stampando del testo qui per dimostrare visivamente quando Rust chiamerà drop.

In main, creiamo due istanze di CustomSmartPointer e poi stampiamo CustomSmartPointers creati. Alla fine di main, le nostre istanze di CustomSmartPointer usciranno dallo scope, e Rust chiamerà il codice che abbiamo inserito nel metodo drop, stampando il nostro messaggio finale. Nota che non abbiamo bisogno di chiamare il metodo drop esplicitamente.

rust

fn main() {
let c = CustomSmartPointer {
data: String::from("alcuni dati"),
};
println!("CustomSmartPointer creato.");
c.drop();
println!("CustomSmartPointer rilasciato prima della fine di main.");
}

Quando proviamo a compilare questo codice, otterremo questo errore:

css

error[E0040]: uso esplicito del metodo distruttore
--> src/main.rs:16:7
|
16 | c.drop();
| --^^^^--
| | |
| | chiamate esplicite del distruttore non consentite
| help: considera di usare la funzione `drop`: `drop(c)`

Questo messaggio di errore afferma che non ci è consentito chiamare esplicitamente drop. Il messaggio di errore utilizza il termine distruttore, che è il termine di programmazione generale per una funzione che pulisce un’istanza. Un distruttore è analogo a un costruttore, che crea un’istanza. La funzione drop in Rust è un particolare distruttore.

Rust non ci permette di chiamare drop esplicitamente perché Rust chiamerebbe comunque automaticamente drop sul valore alla fine di main. Ciò causerebbe un errore di doppia liberazione perché Rust proverebbe a pulire lo stesso valore due volte.

Non possiamo disabilitare l’inserimento automatico di drop quando un valore esce dallo scope, e non possiamo chiamare il metodo drop esplicitamente. Quindi, se dobbiamo forzare la pulizia anticipata di un valore, usiamo la funzione std::mem::drop.

La funzione std::mem::drop è diversa dal metodo drop nel trait Drop. La chiamiamo passando come argomento il valore che vogliamo forzare a rilasciare. La funzione è nel preludio, quindi possiamo modificare main nel Listato 15-15 per chiamare la funzione drop, come mostrato nel Listato 15-16.

rust

fn main() {
let c = CustomSmartPointer {
data: String::from("alcuni dati"),
};
println!("CustomSmartPointer creato.");
drop(c);
println!("CustomSmartPointer rilasciato prima della fine di main.");
}

Eseguendo questo codice verrà stampato:

css

CustomSmartPointer creato.
Rilascio CustomSmartPointer con dati `alcuni dati`!
CustomSmartPointer rilasciato prima della fine di main.

Il testo Rilascio CustomSmartPointer con dati alcuni dati! è stampato tra il testo CustomSmartPointer creato. e CustomSmartPointer rilasciato prima della fine di main., mostrando che il codice del metodo drop viene chiamato per rilasciare c in quel punto.

Puoi usare il codice specificato in un’implementazione del trait Drop in molti modi per rendere comoda e sicura la pulizia: ad esempio, potresti usarlo per creare il tuo allocatore di memoria! Con il trait Drop e il sistema di proprietà di Rust, non devi ricordarti di pulire perché Rust lo fa automaticamente.

Inoltre, non devi preoccuparti dei problemi derivanti dalla pulizia accidentale dei valori ancora in uso: il sistema di proprietà che garantisce che i riferimenti siano sempre validi garantisce anche che drop venga chiamato solo una volta quando il valore non è più in uso.

Ora che abbiamo esaminato Box<T> e alcune delle caratteristiche dei puntatori intelligenti, vediamo alcuni altri puntatori intelligenti definiti nella libreria standard.

Leave a Reply

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *