Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Rust e gestione delle stringhe

Rust e gestione delle stringhe. Cosa sono, quali sono le loro caratteristiche, come nascono e come manipolarle
Rust e gestione delle stringhe. Cosa sono, quali sono le loro caratteristiche, come nascono e come manipolarle
Link copiato negli appunti

Non solo in Rust le stringhe sono uno dei tipi di dato più utilizzati in assoluto in quanto in grado di conservare informazioni di ogni genere. Possono ospitare i caratteri più variegati, dai numeri (che se non usati per indicare quantità andrebbero anch'essi sempre codificati come stringhe) a simboli di qualsiasi alfabeto, soprattutto considerando che un linguaggio evoluto e moderno come Rust contempla caratteri UTF-8.

Di questo tipo di dato potremmo dire che esistono due versioni, gestite in modo differente in memoria e finalizzate a scopi diversi. Per apprezzarne la differenza e non rimanere bloccati da errori (che come sempre Rust lancia per tutelare la memory safety dei nostri programmi) dovremo aver ben chiari i concetti di ownership e borrowing incontrati in una lezione precedente.

Iniziamo quindi a conoscere questo fondamentale tipo di dato entrando nei dettagli della sua dichiarazione.

Come nasce una stringa in Rust

Una stringa fa la sua comparsa nel codice per lo più in due modi:

  • sotto forma di literal tra doppi apici:
    let messaggio = "Tanti auguri di buon Natale!";
  • come dato di tipo String che può essere prodotto a partire da un letterale:
    let messaggio=String::from("Tanti auguri di buon Natale!");

    oppure può essere inizializzata completamente vuota:
    let messaggio=String::new();

Il primo tipo nasce quando abbiamo bisogno di una stringa preimpostata, di dimensione fissa, che non abbiamo intenzione di modificare. La dichiarazione mediante letterale nasce nell'ambito del cosiddetto tipo &str. Quando utilizziamo un dato di tipo String, abbiamo intenzione di creare una stringa di dimensione variabile, molto spesso definita come mut e sulla quale vorremo operare delle modifiche.

A livello di gestione della memoria, possiamo dire che String porta all'ownership, quindi la variabile di definizione possiederà davvero i dati e ad essa sarà legata la loro sopravvivenza mentre str effettua il borrowing e conduce all'uso di reference per la gestione dei dati.

La notazione usata poco fa, &str, ci ricorda un altro costrutto incontrato in una lezione precedente, le slice. Queste sono un meccanismo fondamentale per l'estrazione di porzioni di stringhe.

Slicing

Se vogliamo aprire una view ovvero una finestra su una porzione di stringa per leggerla, analizzarla o sfruttarla in qualche modo, abbiamo bisogno di uno slice. Lo dichiarareremo indicando l'indice iniziale e finale della selezione tra parentesi quadre e separati da una coppia di punti. Il seguente codice, ad esempio:

let s=String::from("Tanti auguri di buon Natale!");
println!("{:?}",s[2..8]);

stampa la stringa nti au prendendo la porzione di testo che va dal secondo carattere, contato a partire da 2, fino al carattere numero 7 visto che 8 rappresenta l'indice del primo elemento non raccolto.

Tra parentesi quadre, è possibile omettere il primo o il secondo indice posizionale e ciò comporterà, rispettivamente, la raccolta della stringa dal primo carattere o dall'indice iniziale alla fine. Il seguente codice:

let s=String::from("Tanti auguri di buon Natale!");
println!("{:?}",&s[..8]);
println!("{:?}",&s[7..]);

stampa infatti:

Tanti au
uguri di buon Natale!

Indexing

In Rust, possiamo accedere all'intero set di caratteri di una stringa mediante la funzione chars. Questo codice stampa in maniera grezza cosa si ottiene:

let s=String::from("Tanti auguri di buon Natale!");
println!("{:?}",s.chars());

.Il risultato è vedere una struttura Chars che contiene una lista fatta di ogni singolo carattere della stringa iniziale:

Chars(['T', 'a', 'n', 't', 'i', ' ', 'a', 'u', 'g', 'u', 'r', 'i', ' ', 'd', 'i', ' ', 'b', 'u', 'o', 'n', ' ', 'N', 'a', 't', 'a', 'l', 'e', '!'])

Saremo in grado da qui di svolgere una sorta di indexing per recuperare un singolo carattere. Ad esempio, potremo avere quello di indice 8 con s.chars().nth(8).

Costruzione di una stringa da zero in Rust

Possiamo avviare la creazione di una stringa da zero e ciò spesso risulta molto utile. Pensiamo alla raccolta o generazione di dati un po' alla volta con la necessità di concatenarli tutti ad una stessa stringa. Ciò sarà possibile perché uno String lavora su memoria heap pertanto viene allocato dinamicamente.

Con la funzione push possiamo concatenare un singolo carattere mentre con push_str una stringa intera. Ecco un esempio:

let mut s=String::new();
s.push('T');
s.push('a');
s.push_str("nti auguri di buon Natale!");

Al momento dell'esecuzione, il contenuto finale di s sarà Tanti auguri di buona Natale! ma notiamo che per fare tutto ciò abbiamo avuto bisogno di dichiarare la stringa come mut in modo da renderla mutabile.

Ti consigliamo anche