Option en Rust
Rust no tiene null. Si un valor puede faltar, eso se dice directamente en el tipo, usando Option. Al principio parece un trámite de más, pero elimina una categoría entera de errores: la famosa “referencia nula”, que ha provocado incontables fallos en otros lenguajes.
Cómo es Option
Sección titulada «Cómo es Option»Option es un enum con dos variantes:
enum Option<T> { Some(T), // hay un valor None, // no hay valor}De hecho, ya lo has visto: .get() en un vector y .get() en un HashMap devuelven un Option.
Crear un Option
Sección titulada «Crear un Option»fn main() { let has_value: Option<i32> = Some(5); let no_value: Option<i32> = None;
println!("{:?}", has_value); println!("{:?}", no_value);}Salida:
Some(5)NoneManejar un Option con match
Sección titulada «Manejar un Option con match»La forma segura de sacar el valor es con match. Rust te obliga a cubrir los dos casos, así que no puedes olvidarte sin querer del caso en el que falta el valor:
fn main() { let age: Option<i32> = Some(30);
match age { Some(value) => println!("La edad es {value}"), None => println!("No se indicó la edad"), }}Un ejemplo más realista
Sección titulada «Un ejemplo más realista»fn find_user(id: u32) -> Option<&'static str> { if id == 1 { Some("Alice") } else { None }}
fn main() { match find_user(1) { Some(name) => println!("Encontrado: {name}"), None => println!("Usuario no encontrado"), }
match find_user(99) { Some(name) => println!("Encontrado: {name}"), None => println!("Usuario no encontrado"), }}Salida:
Encontrado: AliceUsuario no encontradoEn lugar de devolver null o lanzar una excepción cuando no encuentra al usuario, es el propio tipo de retorno de la función el que avisa a quien la llama: “puede que no encuentre nada”. Y el compilador no le deja ignorar esa posibilidad.
Atajos: unwrap_or y unwrap
Sección titulada «Atajos: unwrap_or y unwrap»En los casos sencillos, un match completo puede parecer demasiado. Hay un par de atajos:
fn main() { let age: Option<i32> = None;
let value = age.unwrap_or(0); // si no hay valor, usa 0 println!("{value}");}También existe .unwrap(), que te da el valor directamente, pero provoca un panic (el programa se detiene) si es None:
let age: Option<i32> = Some(30);let value = age.unwrap(); // aquí no hay problema, porque age es SomePráctica
Sección titulada «Práctica»1. El primer número par
Sección titulada «1. El primer número par»Escribe fn first_even(numbers: &[i32]) -> Option<i32>, que devuelva el primer número par del slice, o None si no hay ninguno. Imprime el resultado para [3, 7, 8, 10] con match, y para [1, 3, 5] con .unwrap_or(0). Pista: recorre el slice y haz return Some(n) en cuanto n % 2 == 0; después del bucle, devuelve None.
Ver solución
fn first_even(numbers: &[i32]) -> Option<i32> { for &n in numbers { if n % 2 == 0 { return Some(n); } } None}
fn main() { match first_even(&[3, 7, 8, 10]) { Some(n) => println!("Primer par: {n}"), None => println!("No hay números pares"), }
let fallback = first_even(&[1, 3, 5]).unwrap_or(0); println!("Con valor por defecto: {fallback}");}Salida:
Primer par: 8Con valor por defecto: 02. Buscar un ajuste
Sección titulada «2. Buscar un ajuste»Crea un HashMap de ajustes que solo contenga "volumen" con el valor 80. Después recorre las claves "volumen" y "brillo" e imprime el valor si existe, o un mensaje de “no configurado” si no existe. Pista: .get(key) sobre un HashMap devuelve un Option, así que puedes usar match directamente sobre él.
Ver solución
use std::collections::HashMap;
fn main() { let mut settings = HashMap::new(); settings.insert("volumen", 80);
for key in ["volumen", "brillo"] { match settings.get(key) { Some(value) => println!("{key} = {value}"), None => println!("{key} no está configurado"), } }}Salida:
volumen = 80brillo no está configuradoResumen
Sección titulada «Resumen»Option<T>representa un valor que puede estar o no estar. Rust no tienenull.Some(value)contiene algo;Nonesignifica que no hay nada.matches la forma segura de manejar los dos casos: el compilador no te deja olvidar ninguno..unwrap_or(default)te da un valor de respaldo;.unwrap()provoca un panic si el valor esNone, así que úsalo con moderación.
Comprueba lo que aprendiste
Sección titulada «Comprueba lo que aprendiste»Comprueba lo aprendido
1. ¿Qué usa Rust en lugar de null para un valor que puede faltar?
2. ¿Qué pasa si llamas a .unwrap() sobre un valor None?
3. ¿Qué hace age.unwrap_or(0) cuando age es None?
4. ¿Por qué match se considera la forma segura de sacar el valor de un Option?
5. ¿Cuáles de los métodos mencionados en esta página ya devuelven un Option?
Puntuación: 0 / 5
Para leer más
Sección titulada «Para leer más»- El enum Option y sus ventajas frente a los valores nulos — El libro de Rust (en inglés): la explicación oficial de por qué Rust usa
Optionen lugar denull. std::option— documentación de la biblioteca estándar (en inglés): un repaso de todo lo que puedes hacer con unOption, incluidos métodos útiles comomap,and_theneis_some.- Option & unwrap — Rust by Example (en inglés): ejemplos cortos y ejecutables de
match,unwrapy de cuándo se produce un panic. Option<T>— documentación de std (en inglés): la lista completa de métodos, cada uno con un pequeño ejemplo que puedes copiar.