Rust è un linguaggio di programmazione moderno che combina prestazioni elevate con sicurezza della memoria. Uno degli aspetti più importanti di Rust è la gestione delle stringhe, che può sembrare complessa all’inizio ma offre grande flessibilità e controllo. In questo articolo, esploreremo come funzionano le stringhe in Rust, come si dichiarano, come si manipolano e alcune delle loro caratteristiche uniche.

1. Tipi di Stringhe in Rust

In Rust, ci sono due tipi principali di stringhe:

  • String: Una stringa dinamica, mutabile e allocata sull’heap.
  • &str: Una “string slice”, immutabile e di solito un riferimento a una porzione di una stringa esistente.

1.1. Il Tipo String

Il tipo String è una stringa dinamica che può crescere o ridursi durante l’esecuzione del programma. È allocata sull’heap, il che significa che può contenere una quantità variabile di dati.

Dichiarazione e Inizializzazione

Per creare una nuova String, si può usare il metodo String::new() o la macro to_string():

let mut s = String::new(); // Crea una nuova stringa vuota
let s = "hello".to_string(); // Converte una stringa letterale in una String

Aggiunta di Contenuto

È possibile aggiungere contenuto a una String utilizzando il metodo push_str o push:

s.push_str(" world"); // Aggiunge una stringa slice
s.push('!'); // Aggiunge un singolo carattere

Concatenazione

Per concatenare stringhe, si può usare l’operatore + o la macro format!:

let s1 = String::from("Hello");
let s2 = String::from(" world");
let s3 = s1 + &s2; // Nota: s1 viene consumato, s2 viene preso come riferimento

let s4 = format!("{}{}", s2, s3); // Crea una nuova stringa senza consumare s2 e s3

1.2. Il Tipo &str

Il tipo &str è una “string slice”, ovvero un riferimento immutabile a una porzione di una stringa. È spesso utilizzato per riferirsi a parti di una String o a stringhe letterali.

Dichiarazione e Utilizzo

Le stringhe letterali in Rust sono di tipo &str:

let s: &str = "hello"; // s è una stringa slice

È anche possibile creare una slice da una String:

let s1 = String::from("hello world");
let s2: &str = &s1[0..5]; // s2 è una slice che contiene "hello"

Iterazione su una Slice

Le stringhe in Rust sono codificate in UTF-8, quindi è possibile iterare sui caratteri di una stringa slice:

for c in s2.chars() {
    println!("{}", c);
}

2. Operazioni Comuni sulle Stringhe

Rust fornisce una serie di metodi utili per manipolare le stringhe. Ecco alcune operazioni comuni:

2.1. Lunghezza di una Stringa

Per ottenere la lunghezza di una stringa, si usa il metodo len:

let len = s2.len(); // Restituisce la lunghezza in byte

2.2. Accesso ai Caratteri

Per accedere ai caratteri di una stringa, si può usare il metodo chars:

let primo_carattere = s2.chars().nth(0); // Restituisce il primo carattere come Option<char>

2.3. Sostituzione di Sottostringhe

Per sostituire una sottostringa, si usa il metodo replace:

let nuova_stringa = s2.replace("hello", "ciao"); // Sostituisce "hello" con "ciao"

2.4. Divisione di una Stringa

Per dividere una stringa in parti, si usa il metodo split:

let parti: Vec<&str> = s2.split(' ').collect(); // Divide la stringa in base agli spazi

3. Gestione degli Errori con le Stringhe

In Rust, le operazioni sulle stringhe possono fallire, specialmente quando si lavora con indici o caratteri non validi. È importante gestire questi errori in modo sicuro.

3.1. Accesso Sicuro agli Indici

Per accedere a un carattere in modo sicuro, si può usare il metodo get:

if let Some(c) = s2.get(0..1) {
    println!("{}", c); // Stampa il primo carattere
}

3.2. Gestione dei Caratteri Non Validi

Rust è molto attento alla gestione dei caratteri non validi in UTF-8. Se si tenta di accedere a un indice non valido, il programma potrebbe andare in panic. È sempre meglio usare metodi sicuri come chars o get.

4. Conclusioni

Le stringhe in Rust sono potenti e flessibili, ma richiedono una buona comprensione dei loro due tipi principali: String e &str. Con questa guida, hai ora una solida base per lavorare con le stringhe in Rust, dalla dichiarazione alla manipolazione avanzata. Ricorda sempre di gestire gli errori in modo sicuro e di sfruttare i metodi forniti da Rust per lavorare in modo efficiente con i dati testuali.