Enumeraciones en Rust
Las enumeraciones en Rust no son tan simples como el concepto en otros lenguajes de programación, pero aún así se pueden usar de manera muy sencilla:
Ejemplo
enum Book {
Papery, Electronic
}
fn main() {
let book = Book::Papery;
println!("{:?}", book);
}
Resultado de ejecución:
Papery
Los libros se dividen en libros en papel (Papery book) y libros electrónicos (Electronic book).
Si ahora estás desarrollando un sistema de gestión de bibliotecas, necesitas describir los diferentes atributos de los dos tipos de libros (el libro en papel tiene un número de clasificación, el libro electrónico solo tiene una URL). Puedes agregar descripciones de atributos de tupla a los miembros de la enumeración:
enum Book {
Papery(u32),
Electronic(String),
}
let book = Book::Papery(1001);
let ebook = Book::Electronic(String::from("url://..."));
Si deseas nombrar los atributos, puedes usar la sintaxis de estructura:
enum Book {
Papery { index: u32 },
Electronic { url: String },
}
let book = Book::Papery{index: 1001};Aunque se pueden nombrar de esta manera, ten en cuenta que no se pueden acceder a los atributos vinculados a la enumeración como si se accediera a los campos de una estructura. El método de acceso está en la sintaxis de match.
Sintaxis de match
El propósito de las enumeraciones es clasificar un cierto tipo de cosas, y el propósito de la clasificación es describir diferentes situaciones. Basado en este principio, a menudo las enumeraciones terminan siendo procesadas por estructuras de ramificación (switch en muchos lenguajes). La sintaxis de switch es clásica, pero no es compatible con Rust. Muchos lenguajes descartan switch porque es propenso a problemas de ejecución en cascada debido a olvidar agregar break. Lenguajes como Java y C# evitan esta situación mediante verificaciones de seguridad.
Rust implementa estructuras de ramificación mediante la sentencia match. Primero veamos cómo usar match para manejar enumeraciones:
Ejemplo
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);
}
}
}
Resultado de ejecución:
Papery book 1001
El bloque match también se puede tratar como una expresión de función, y también puede tener un valor de retorno:
match 枚举类实例 {
分类1 => 返回值表达式,
分类2 => 返回值表达式,
...
}¡Pero los tipos de todas las expresiones de retorno deben ser iguales!
Si los atributos adicionales de la enumeración se definen como una tupla, en el bloque match se debe especificar un nombre temporal:
Ejemplo
Papery(u32),
Electronic {url: String},
}
let book = Book::Papery(1001);
match book {
Book::Papery(i) => {
println!("{}", i);
},
Book::Electronic { url } => {
println!("{}", url);
}
}
Además de poder seleccionar ramas para enumeraciones, match también puede seleccionar ramas para datos de tipos como enteros, números de punto flotante, caracteres y referencias de segmentos de cadena (&str). Entre ellos, aunque es legal usar números de punto flotante para la selección de ramas, no se recomienda hacerlo, porque los problemas de precisión pueden causar errores de rama.
Al seleccionar ramas para tipos que no son enumeraciones, se debe prestar atención al manejo de casos excepcionales, incluso si no hay nada que hacer en el caso excepcional..Los casos excepcionales se indican con un guion bajo_para representar:
Ejemplo
let t = "abc";
match t {
"abc" => println!("Yes"),
_ => {},
}
}
La enumeración Option
Option es una enumeración en la biblioteca estándar de Rust. Esta clase se utiliza para llenar el vacío de que Rust no admite referencias null.
Muchos lenguajes admiten la existencia de null (C/C++, Java), lo cual es muy conveniente, pero también crea enormes problemas. El inventor de null también reconoció esto: 'una idea conveniente causó pérdidas acumuladas de mil millones de dólares'.
null a menudo da un golpe mortal al programa cuando el desarrollador trata todo como si no fuera null: después de todo, solo basta un error de este tipo para que la ejecución del programa se termine por completo.
Para resolver este problema, muchos lenguajes no permiten null por defecto, pero admiten la aparición de null a nivel del lenguaje (a menudo modificado con el símbolo ? antes del tipo).
Java admite null por defecto, pero puede restringir la aparición de null mediante la anotación @NotNull, que es una forma de solución temporal.
Rust no permite la existencia de valores nulos null a nivel del lenguaje, pero como null puede resolver de manera eficiente algunos problemas, Rust introduce la enumeración Option:
enum Option<T> {
Some(T),
None,
}Si deseas definir una clase que pueda tener un valor nulo, puedes hacerlo así:
let opt = Option::Some("Hello");Si deseas realizar ciertas operaciones sobre opt, primero debes determinar si esOption::None:
Ejemplo
let opt = Option::Some("Hello");
match opt {
Option::Some(something) => {
println!("{}", something);
},
Option::None => {
println!("opt is nothing");
}
}
}
Resultado de ejecución:
Hello
Si tu variable comienza siendo un valor nulo, sé comprensivo con el compilador: ¿cómo sabe qué tipo es la variable cuando el valor no es nulo?
Por lo tanto, un Option con valor inicial nulo debe especificar su tipo:
Ejemplo
let opt: Option<&str> = Option::None;
match opt {
Option::Some(something) => {
println!("{}", something);
},
Option::None => {
println!("opt is nothing");
}
}
}
Resultado de ejecución:
opt is nothing
Este diseño hace que la programación con valores nulos sea más difícil, pero eso es precisamente lo que se necesita para construir un sistema estable y eficiente. Como Option es introducido por defecto por el compilador de Rust, al usarlo se puede omitir Option:: y escribir directamente None o Some().
Option es un tipo especial de enumeración que puede contener valores para la selección de ramas:
Ejemplo
let t = Some(64);
match t {
Some(64) => println!("Yes"),
_ => println!("No"),
}
}
Sintaxis de if let
Ejemplo
match i {
0 => println!("zero"),
_ => {},
}
Resultado de ejecución en la función principal:
zero
El propósito de este programa es determinar si i es el número 0, y si lo es, imprimir zero.
Ahora acorta este código usando la sintaxis de if let:
let i = 0;
if let 0 = i {
println!("zero");
}El formato de la sintaxis de if let es el siguiente:
if let 匹配值 = 源变量 {
语句块
}Se puede agregar un bloque else después para manejar los casos excepcionales.
La sintaxis de if let puede considerarse 'azúcar sintáctico' de la sentencia match que solo distingue dos casos (el azúcar sintáctico se refiere a un sustituto conveniente con el mismo principio que cierta sintaxis).
También es aplicable a las enumeraciones:
Ejemplo
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");
}
}