English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Le enumerazioni in Rust non sono così semplici come nei concetti di altri linguaggi di programmazione, ma possono essere utilizzate molto semplicemente:
#[derive(Debug)]
enum Book {
Papery, Electronic
{}
fn main() {
let book = Book::Papery;
println!("{:?}", book);
{}
Risultato dell'esecuzione:
Papery
I libri si dividono in libri cartacei (Papery book) e libri elettronici (Electronic book).
Se stai sviluppando un sistema di gestione dei libri, devi descrivere le proprietà diverse di due tipi di libri (i libri cartacei hanno un numero di catalogo, mentre i libri elettronici hanno solo l'URL), puoi aggiungere descrizioni di attributi tuple ai membri dell'enumerazione:
enum Book { Papery(u32), Electronic(String), {} let book = Book::Papery(1001); let ebook = Book::Electronic(String::from("url://..."));
Se desideri assegnare un nome a un attributo, puoi utilizzare la sintassi delle strutture:
enum Book { Papery { index: u32 }, Electronic { url: String }, {} let book = Book::Papery{index: 1001};
Anche se è possibile chiamarlo così, si prega di notare che non è possibile accedere alle proprietà di un'enumerazione come se fossero campi di una struttura. Il metodo di accesso è nel sintassi match.
L'obiettivo degli enumerabili è la classificazione di un tipo di cosa, e l'obiettivo della classificazione è descrivere diverse situazioni. Sulla base di questo principio, spesso gli enumerabili vengono trattati con strutture di branca (switch in molti linguaggi). La sintassi del switch è classica, ma non è supportata in Rust. Molte lingue hanno abbandonato il switch per evitare problemi di esecuzione in cascata causati dall'omissione di break, Java e C# evitano questo problema attraverso controlli di sicurezza.
Rust utilizza l'istruzione match per implementare la struttura di branca. Familiarizziamoci con come utilizzare match per gestire classi enumerabili:
fn main() {
enum Book {
Papery {index: u32},
Electronic {url: String},
{}
let book = Book::Papery{index: 1001};
let ebook = Book::Electronic{url: String::from("url...")};
match book {
Book::Papery { index } => {
println!("Papery book {}", index);
},
Book::Electronic { url } => {
println!("E-book {}", url);
{}
{}
{}
Risultato dell'esecuzione:
Papery book 1001
Il blocco match può anche essere trattato come un'espressione di funzione, e può anche avere un valore di ritorno:
match esempio di classe enumerabile { Categorizzazione 1 => espressione di valore di ritorno, Categorizzazione 2 => espressione di valore di ritorno, ... {}
Ma tutti gli espressioni di valore di ritorno devono avere lo stesso tipo!
Se si definisce un attributo aggiuntivo di classe enumerabile come tuple, è necessario specificare temporaneamente un nome nel blocco match:
enum Book {
Papery(u32),
Electronic {url: String},
{}
let book = Book::Papery(1001);
match book {
Book::Papery(i) => {
println!("{}", i);
},
Book::Electronic { url } => {
println!("{}", url);
{}
{}
Oltre a poter effettuare scelte di branca per classi enumerabili, match può anche effettuare scelte di branca su dati di tipo intero, numero a virgola mobile, carattere e slice di stringa (&str). Anche se è legittimo utilizzare la scelta di branca per il tipo numero a virgola mobile, non è raccomandato farlo a causa dei problemi di precisione che possono portare a errori di branca.
Quando si effettua una scelta di branca per una classe non enumerata, deve essere prestata attenzione alla gestione delle eccezioni, anche se non ci sono cose da fare in caso di eccezione. Le eccezioni sono rappresentate con l'underscore _.
fn main() {
let t = "abc";
match t {
"abc" => println!("Sì"),
_ => {},
{}
{}
Option è un tipo enumerativo della libreria standard di Rust, utilizzato per colmare il vuoto di supporto per i riferimenti nulli in Rust.
Molti linguaggi supportano l'esistenza di null (C/C++, Java), il che è comodo, ma crea anche problemi enormi, l'inventore di null ha anche ammesso questo, "un'idea comoda che ha causato una perdita cumulativa di 1 miliardo di dollari".
Null spesso colpisce il programma quando lo sviluppatore assume che tutto non sia null: dopo tutto, una sola di queste errori può portare alla completa interruzione del funzionamento del programma.
Per risolvere questo problema, molti linguaggi non permettono di default null, ma supportano la sua presenza a livello linguistico (solitamente indicato con il simbolo ? davanti al tipo).
Java supporta di default null, ma può essere limitato con l'annotazione @NotNull, che è una soluzione di compromesso.
Rust non permette affatto l'esistenza di valori nulli a livello linguistico, ma purtroppo null può risolvere efficacemente problemi少量 di problemi, quindi Rust ha introdotto l'enumerazione Option:
enum Option<T> { Some(T), None, {}
Se vuoi definire una classe che può essere nulla, puoi farlo così:
let opt = Option::Some("Ciao");
Se vuoi eseguire alcune operazioni su opt, devi prima determinare se è Option::None:
fn main() {
let opt = Option::Some("Ciao");
match opt {
Option::Some(qualcosa) => {
println!("{}", qualcosa);
},
Option::None => {
println!("opt non è nulla");
{}
{}
{}
Risultato dell'esecuzione:
Ciao
Se la tua variabile inizia come valore nullo, comprensione per il compilatore, come sa qual è il tipo della variabile quando il valore non è nullo?
Quindi l'Option inizialmente vuoto deve specificare chiaramente il tipo:
fn main() {
let opt: Option<&str> = Option::None;
match opt {
Option::Some(qualcosa) => {
println!("{}", qualcosa);
},
Option::None => {
println!("opt non è nulla");
{}
{}
{}
Risultato dell'esecuzione:
opt non è nulla
Questo design rende difficile la programmazione con valori nulli, ma è proprio quello di cui ha bisogno per costruire un sistema stabile ed efficiente. Poiché Option è introdotto di default dal compilatore Rust, può essere omesso di scrivere Option:: direttamente None o Some().
Option è una classe enum speciale che può avere ramificazioni di scelta con valori: }}
fn main() {
let t = Some(64);
match t {
Some(64) => println!("Yes"),
_ => println!("No"),
{}
{}
let i = 0;
match i {
0 => println!("zero"),
_ => {},
{}
Eseguire il programma principale e vedere i risultati:
zero
L'obiettivo di questo programma è determinare se i è il numero 0, e se lo è, stampare zero.
Ora abbreviamo questo codice con la sintassi if let:
let i = 0; if let 0 = i { println!("zero"); {}
La sintassi if let è come segue:
if let valore_corrispondente = variabile_sorgente { Blocco di istruzioni {}
È possibile aggiungere un blocco else per gestire le eccezioni.
La sintassi if let può essere considerata una "dolcezza" di un'istruzione match che distingue solo due casi (dolcezza di sintassi è un'alternativa conveniente con principi di sintassi identici).
Per le classi enum è applicabile anche:
fn main() {
enum Book {
Papery(u32),
Electronic(String)
{}
let book = Book::Electronic(String::from("url"));
if let Book::Papery(index) = book {
println!("Papery {}", index);
else {
println!("Not papery book");
{}
{}