(… oggi ho imparato che … premessa)
Cosa vuol dire “lifetime” in Rust:
A lifetime parameter does not allow you to choose how long a value lives, it only allows you to communicate to the compiler that two or more references are “related” to the same memory and are expected to share the same lifetime.
https://stackoverflow.com/a/28109180/250970
Quindi si parla di references (indicate con &T, dove T è un tipo) ad una locazione di memoria.
Un riferimento ad una struttura dinamica perde valità se la struttura muta
Concetto che ha degli esempi già nel Rust book, ma che sembra naturale quando si affronta un problema.
qua il messaggio di errore sembra essere relativo al lifetime, ma in realtà l’annotazione è giusta, bstr
vivrebbe abbastanza, a patto che self.names non cambi.
Se invece applico il lifetime &'a mut self
alla funzione?
Un lifetime di un riferimento mut troppo prolungato blocca la struttura a cui si riferisce
Applicando il lifetime ‘a (&'a mut self
) mut self resta in vita tanto quanto è l’aspettativa di vita della struct HaGraph.
questa volta il messaggio di errore è più aderente a quello che succede:
error[E0499]: cannot borrow g
as mutable more than once at a time
infatti g
è catturato “a vita” dal &mut self.
Questo spiega anche il fatto che una singola chiamata get_or_push
funziona correttamente: dal punto di vista del compliatore, non c’è possibilità che self.names cambi, può essere controllato staticamente, visto che ha il lifetime uguale a quello della struttura (ossia ‘a).
Se invece il compilatore si accorge che cambia, come in https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=cf915a88e7a47296e2e786aa735a3f51
allora riporta immediatamente l’errore.
Un altro esempio dove è più chiaro che il problema è nel “mut catturato a lifetime”:
è più corretto chiamarlo: “riferimento mut catturato per l’intero lifetime”
E gli altri linguaggi? non è naturale ragionare così?
Quello che mi ha un po’ deluso, e poi sorpreso è che questo modo di procedere mi sembrava del tutto naturale: ho una lista di nodi, li inserisco nel grafo, ed in una hashmap, di modo che sono sicuro che non siano già presenti, e uso dei riferimenti, cosicché evito di duplicare inutilmente i dati.
Ragionamento naturale, ma la falla è nella struttura che mantiene i dati reali, e nel fatto che li sto inserendo uno alla volta.
A questo punto, quello che invece è naturale, è creare una struttura “statica”, che contenga tutte le string, uso le virgolette perché String è dinamico, ma il suo contenitore è un “array di puntatori” di dimensione fissa. È necessario anche avere un contatore che indica quante posizioni sono state occupate, ma tutto questo resta incapsulato.
La domanda resta: ma se con il C++ posso farlo e non va in crash, perché con Rust no?
Il fatto che non vada in crash col C++ “facendo diverse prove”, non vuol dire che non darà errore di riferimento in altre situazioni.
Conclusione
Se puoi farlo con altri linguaggi, probabilmente lo stai facendo male con altri linguaggi.