El Enum Option revisitado
Parte de la sección Object Oriented Programming del Journey de Rust de Coddy — lección 17 de 61.
Ya te has encontrado con Option<T> en Rust; es el enum estándar para representar valores que pueden existir o no. Ahora veámoslo a través de una lente de OOP: Option no es solo un enum, es un tipo con potentes métodos integrados que te permiten manejar valores nulos de forma segura y elegante.
En lugar de usar coincidencia de patrones cada vez, puedes usar métodos directamente en valores Option. El método unwrap_or devuelve el valor interno si existe, o un valor predeterminado que proporciones:
let maybe_name: Option<String> = Some(String::from("Alice"));
let name = maybe_name.unwrap_or(String::from("Guest"));
println!("{}", name); // Alice
let empty: Option<String> = None;
let fallback = empty.unwrap_or(String::from("Guest"));
println!("{}", fallback); // Guest
El método map transforma el valor interno si existe, dejando None sin cambios. Esto te permite encadenar operaciones sin tener que comprobar constantemente None:
let maybe_num: Option<i32> = Some(5);
let doubled = maybe_num.map(|x| x * 2);
println!("{:?}", doubled); // Some(10)
let nothing: Option<i32> = None;
let still_nothing = nothing.map(|x| x * 2);
println!("{:?}", still_nothing); // None
Estos métodos encarnan los principios de la OOP: el tipo Option encapsula el concepto de "tal vez un valor" y proporciona una interfaz limpia para trabajar con él. Se llaman a los métodos en el objeto en lugar de escribir lógica externa para inspeccionarlo.
Desafío
FácilVamos a construir un sistema de perfil de usuario que maneje con elegancia los datos opcionales utilizando métodos de Option. Los usuarios pueden tener un apodo establecido o no; tu sistema manejará ambos casos de forma elegante sin necesidad de un emparejamiento explícito.
Crearás dos archivos para organizar tu código:
profile.rs: Define una estructura públicaProfilecon dos campos:username(String) ynickname(Option<String>). Implementa:- Un constructor
newque reciba un nombre de usuario y un apodo opcional - Un método
display_nameque devuelva el apodo si existe, o el nombre de usuario como alternativa; utilizaunwrap_orpara manejar esto - Un método
formatted_nicknameque devuelva unOption<String>con el apodo envuelto entre corchetes (como"[CoolNick]") si existe, oNonesi no hay apodo; utilizamappara transformar el valor
- Un constructor
main.rs: Incorpora tu módulo de perfil y crea dos perfiles: uno con apodo y otro sin él. Demuestra ambos métodos en cada perfil para mostrar cómo los métodos deOptionmanejan los valores presentes y ausentes de manera diferente.
Para el método formatted_nickname, utiliza el formato de depuración ({:?}) al imprimir, ya que devuelve un Option.
Tu salida debe seguir este formato exacto, mostrando ambos perfiles:
Display name: {name}
Formatted nickname: {option}
Display name: {name}
Formatted nickname: {option}Por ejemplo, con un perfil para el nombre de usuario "alice" con el apodo "Ace", y otro para el nombre de usuario "bob" sin apodo, la salida sería:
Display name: Ace
Formatted nickname: Some("[Ace]")
Display name: bob
Formatted nickname: NoneRecibirás tres entradas: el primer nombre de usuario, el apodo del primer usuario y el segundo nombre de usuario (que no tiene apodo).
Hoja de referencia
El tipo Option<T> proporciona métodos para trabajar con valores opcionales sin necesidad de una coincidencia explícita.
El método unwrap_or devuelve el valor interno si existe, o un valor predeterminado si es None:
let maybe_name: Option<String> = Some(String::from("Alice"));
let name = maybe_name.unwrap_or(String::from("Guest"));
println!("{}", name); // Alice
let empty: Option<String> = None;
let fallback = empty.unwrap_or(String::from("Guest"));
println!("{}", fallback); // Guest
El método map transforma el valor interno si existe, dejando None sin cambios:
let maybe_num: Option<i32> = Some(5);
let doubled = maybe_num.map(|x| x * 2);
println!("{:?}", doubled); // Some(10)
let nothing: Option<i32> = None;
let still_nothing = nothing.map(|x| x * 2);
println!("{:?}", still_nothing); // None
Estos métodos permiten encadenar operaciones sobre valores Option sin tener que comprobar constantemente si es None.
Pruébalo tú mismo
mod profile;
use profile::Profile;
fn main() {
// Leer entradas
let mut input1 = String::new();
std::io::stdin().read_line(&mut input1).expect("Failed to read line");
let username1 = input1.trim().to_string();
let mut input2 = String::new();
std::io::stdin().read_line(&mut input2).expect("Failed to read line");
let nickname1 = input2.trim().to_string();
let mut input3 = String::new();
std::io::stdin().read_line(&mut input3).expect("Failed to read line");
let username2 = input3.trim().to_string();
// TODO: Crear el primer perfil con username1 y nickname1 (Some)
// TODO: Crear el segundo perfil con username2 y sin apodo (None)
// TODO: Imprimir display_name y formatted_nickname para el primer perfil
// Usa {:?} para formatted_nickname ya que devuelve un Option
// TODO: Imprimir display_name y formatted_nickname para el segundo perfil
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Object Oriented Programming
1Métodos y Comportamiento
Intro a Bloques de ImplementaciónEl Parámetro SelfMétodos MutablesFunciones AsociadasMúltiples Bloques de ImplementaciónEncadenamiento de MétodosResumen - Acciones de Rectángulo4Proyecto: Mascota virtual
Definir la mascotaAlimentar a la mascota7Traits estándar
El trait DebugEl trait DisplayClone y CopyTraits de igualdadResumen - Punto imprimible10Proyecto: Sistema de documentos
El Trait DrawComponente de texto2Encapsulamiento y módulos
Conceptos básicos de módulosLa palabra clave PublicCampos privadosGettersSettersRepaso - Casillero seguro5Genéricos
Estructuras genéricasMétodos genéricosMúltiples tipos genéricosFunciones genéricasResumen - Punto de coordenadas8Traits como límites
Sintaxis de límites de traitsLímites múltiplesLa cláusula whereRetornar tipos con traitsResumen: Impresora genérica11Patrones de diseño en Rust
Patrón NewtypeComposiciónEl Trait DropFrom e IntoResumen - Mock de Smart Pointer3Enums avanzados
Enums con datosMétodos en EnumsCoincidencia de variantes de datosEl Enum Option revisitadoResumen - Enum Shape6Definición de Traits
¿Qué es un Trait?Implementación de TraitsImplementaciones por defectoSobrescribir valores por defectoTraits con parámetrosResumen - Reproductor multimedia