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 la enumeración estándar para representar valores que pueden existir o no. Ahora veámoslo desde la perspectiva de la POO: Option no es solo una enumeración, sino un tipo con métodos integrados potentes que te permiten manejar valores anulables de forma segura y elegante.
En lugar de comprobar cada vez, puedes usar métodos directamente en valores de 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 si hay un valor 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ás haya 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 con elegancia los datos opcionales mediante métodos de Option. Los usuarios pueden tener un apodo o no. Tu sistema gestionará ambos casos de forma elegante y 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, como alternativa, el nombre de usuario: 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 el módulo del perfil y crea dos perfiles: uno con apodo y otro sin él. Muestra ambos métodos para cada perfil para ver 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 este formato exacto y mostrar ambos perfiles:
Display name: {name}
Formatted nickname: {option}
Display name: {name}
Formatted nickname: {option}Por ejemplo, para un perfil con el nombre de usuario "alice" y el apodo "Ace", y otro con 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() {
// Lee las 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: Crea el primer perfil con username1 y nickname1 (Some)
// TODO: Crea el segundo perfil con username2 y sin apodo (None)
// TODO: Imprime display_name y formatted_nickname del primer perfil
// Usa {:?} para formatted_nickname, ya que devuelve un Option
// TODO: Imprime display_name y formatted_nickname del segundo perfil
}
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