El Enum Option revisitado
Parte de la sección Programación Orientada a Objetos 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 desde la perspectiva de la POO: Option no es solo un enum, sino un tipo con potentes métodos integrados que te permiten manejar valores anulables de forma segura y elegante.
En lugar de hacer una coincidencia cada vez, puedes usar métodos directamente sobre los valores de Option. El método unwrap_or devuelve el valor interno si exists, 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 comprobar constantemente si es 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 POO: el tipo Option encapsula el concepto de «quizá un valor» y proporciona una interfaz clara para trabajar con él. Llamas a métodos del objeto en lugar de escribir lógica externa para inspeccionarlo.
Desafío
FácilConstruyamos un sistema de perfiles de usuario que gestione correctamente los datos opcionales mediante métodos de Option. Los usuarios pueden tener un apodo configurado o no tenerlo. Tu sistema gestionará ambos casos de forma elegante sin coincidencias explícitas.
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: usaunwrap_orpara gestionar esto - Un método
formatted_nicknameque devuelva unOption<String>con el apodo entre corchetes (como"[CoolNick]") si existe, oNonesi no hay apodo: usamappara transformar el valor
- Un constructor
main.rs: Incluye 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 deOptiongestionan de forma diferente los valores presentes y ausentes.
Para el método formatted_nickname, usa el formato de depuración ({:?}) al imprimir, ya que devuelve un Option.
Tu salida debe seguir exactamente este formato, 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).
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: Create second profile with username2 and no nickname (None)
// TODO: Print display_name and formatted_nickname for first profile
// Use {:?} for formatted_nickname since it returns an Option
// TODO: Print display_name and formatted_nickname for second profile
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
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 multimediaPractica por tu cuenta: Compilador de Rust online