English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Clases de enumeración en Rust

Las clases enumeradas en Rust no son tan simples como en otros lenguajes de programación, pero aún se pueden usar de manera muy sencilla:

#[derive(Debug)]

enum Book {
    Papery, Electrónico
}

fn main() {
    let book = Book::Papery;
    println!("{:?}", book);
}

Resultado de ejecución:

Papery

Los libros se dividen en libros en papel (libro en papel) y libros electrónicos (libro electrónico).

Si estás desarrollando actualmente un sistema de gestión de libros, necesitarás describir las diferentes propiedades de dos tipos de libros (los libros en papel tienen un número de registro, y los libros electrónicos solo tienen una URL), puedes agregar propiedades descriptivas de tuplas a los miembros de la clase enumerada:

enum Book {
    Papery(u32),
    Electrónico(String),
}
let book = Book::Papery(1001);
let ebook = Book::Electronic(String::from("url://..."));

Si deseas nombrar las propiedades, puedes usar la sintaxis de estructura:

enum Book {
    Papery { index: u32 },
    Electrónico { url: String },
}
let book = Book::Papery{index: 1001};

虽然可以如此命名,但请注意,并不能像访问结构体字段一样访问枚举类绑定的属性。访问的方法在 match 语法中。

match 语法

枚举的目的是对某一类事物进行分类,分类的目的是为了描述不同的情况。基于这个原理,枚举类最终通常都会被分支结构处理(许多语言中的 switch)。switch 语法很经典,但在 Rust 中并不支持,很多语言摒弃 switch 的原因都是因为 switch 容易存在因忘记添加 break 而产生的连续运行问题,Java 和 C# 这类语言通过安全检查来避免这种情况的发生。

Rust 通过 match 语句来实现分支结构。先认识一下如何用 match 处理枚举类:

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 } => &123;
            println!("Papery book {}", index);
        },
        Book::Electronic { url } => &123;
            println!("E-book {}", url);
        }
    }
}

运行结果:

Papery book 1001

match 块也可以当作函数表达式来对待,它也是有返回值的:

match 枚举类示例 {
    分类1 => 返回值表达式,
    分类2 => 返回值表达式,
    ...
}

但是所有返回值表达式的类型必须相同!

如果将枚举类的附加属性定义为元组,在 match 块中需要临时指定一个名字:

enum Book {
    Papery(u32),
    Electronic {url: String},
}
let book = Book::Papery(1001);

match book {
    Book::Papery(i) => &123;
        println!("{}", i);
    },
    Book::Electronic { url } => &123;
        println!("{}", url);
    }
}

match 除了能够对枚举类进行分支选择以外,还可以对整数、浮点数、字符和字符串切片引用(&str)类型的数据进行分支选择。其中,浮点数类型被分支选择虽然合法,但不推荐这样使用,因为精度问题可能会导致分支错误。

在选择非枚举类进行分支时必须注意处理例外情况,即使在例外情况下没有任何要做的事。例外情况用下划线 _ 表示:

fn main() {
    let t = "abc";
    match t {
        "abc" => println!&40;"Sí"),
        _ => {},
    }
}

Clase de enumeración Option

Option es una clase de enumeración de la biblioteca estándar de Rust, esta clase se utiliza para llenar el vacío de la falta de soporte de referencias null en Rust.

Muchos lenguajes admiten la existencia de null (C/C++、Java),esto es muy conveniente, pero también crea muchos problemas, el inventor de null también reconoce esto, "una idea conveniente causó una acumulación 10 pérdida de miles de millones de dólares".

null a menudo da a los programas un golpe mortal cuando los desarrolladores consideran todo como no null: después de todo, con un solo error de este tipo, la ejecución del programa se detendrá completamente.

Para resolver este problema, muchos lenguajes no permiten por defecto la existencia de null, pero admiten su aparición a nivel de lenguaje (a menudo se indica con el símbolo ? delante del tipo).

Java admite por defecto null, pero se puede limitar la aparición de null mediante la anotación @NotNull, lo que es una solución provisional.

Rust no permite la existencia de valores nulos null a nivel de lenguaje, pero, desafortunadamente, null puede resolver problemas少量的 de manera eficiente, por lo que Rust introdujo la clase de enumeración Option:

enum Option<T> {
    Some(T),
    None,
}

Si deseas definir una clase que puede tener valores nulos, puedes hacerlo así:

let opt = Option::Some("Hola");

Si deseas realizar alguna operación sobre opt, debes primero determinar si es Option::None:

fn main() {
    let opt = Option::Some&40;"Hola"&41;;
    match opt &123;
        Option::Some&40;algo&41; => &123;
            println!("{}", algo&41;;
        },
        Option::None => &123;
            println!("opt es nada"&41;;
        }
    }
}

Resultado de ejecución:

Hola

Si tu variable comienza siendo un valor nulo, ten compasión con el compilador, ¿cómo sabe el compilador qué tipo es la variable cuando el valor no es nulo?

Por lo tanto, el Option inicialmente vacío debe especificar el tipo de manera explícita:

fn main() {
    let opt: Option<&str> = Option::None;
    match opt &123;
        Option::Some&40;algo&41; => &123;
            println!("{}", algo&41;;
        },
        Option::None => &123;
            println!("opt es nada"&41;;
        }
    }
}

Resultado de ejecución:

opt es nada

Este diseño hace que la programación de valores nulos sea difícil, pero es exactamente lo que se necesita para construir un sistema estable y eficiente. Dado que Option se introduce por defecto por el compilador Rust, se puede omitir Option:: y escribir directamente None o Some().

Option es una clase de enumeración especial que puede contener ramas de valor de elección:

fn main() {
        let t = Some(64);
        match t {
                Some(64) => println!("Sí"),
                _ => println!("No"),
        }
}

Sintaxis if let

let i = 0;
match i {
    0 => println!("zero"),
    _ => {},
}

Ejecutar en la función principal y obtener el resultado:

zero

El objetivo de este programa es determinar si i es el número 0, y si lo es, imprimir zero.

Ahora acortemos este código con la sintaxis if let:

let i = 0;
if let 0 = i {
    println!("zero");
}

La forma de sintaxis de if let es la siguiente:

if let valor = variableFuente {
    Bloque de instrucciones
}

Se puede agregar un bloque else para manejar casos excepcionales.

La sintaxis if let puede considerarse como un "edulcorante" (un sustituto conveniente de una sintaxis con principios idénticos) de una sentencia match que distingue solo dos casos.

Para las clases de enumeración también es aplicable:

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!("No es un libro de papel");
    }
}