Retornar tipos con traits
Parte de la sección Programación Orientada a Objetos del Journey de Rust de Coddy. Lección 43 de 61.
Has utilizado límites de traits para restringir qué tipos puede aceptar una función. Pero ¿qué ocurre con lo que una función devuelve? La sintaxis impl Trait te permite especificar que una función devuelve «algún tipo que implementa este trait» sin nombrar el tipo concreto.
Aquí tienes la sintaxis en acción:
trait Summary {
fn summarize(&self) -> String;
}
struct Article {
headline: String,
}
impl Summary for Article {
fn summarize(&self) -> String {
self.headline.clone()
}
}
fn create_summary() -> impl Summary {
Article { headline: String::from("Breaking News!") }
}
El tipo de retorno impl Summary indica a quienes realizan la llamada «obtendrás algo que implementa Summary». Quien realiza la llamada puede usar cualquier método del trait, pero no necesita saber que el tipo real es Article.
Esto es especialmente útil cuando el tipo concreto es complejo o cuando quieres ocultar los detalles de implementación. La función promete una capacidad (el trait), no un tipo específico.
Sin embargo, hay una limitación importante: la función debe devolver exactamente un tipo concreto. No puedes devolver condicionalmente tipos diferentes que implementen el mismo trait. Eso requiere objetos de trait, que aprenderás más adelante.
// Esto funciona - siempre devuelve Article
fn make_item() -> impl Summary {
Article { headline: String::from("Hello") }
}
// Esto NO compilaría - dos tipos diferentes
// fn make_item(flag: bool) -> impl Summary {
// if flag { Article { ... } } else { Tweet { ... } }
// }
Desafío
Fácil¡Construyamos una fábrica de tarjetas de felicitación que use el tipo de retorno impl Trait para ocultar los detalles de implementación! Crearás una función que devuelva «algo que pueda saludar» sin revelar el tipo concreto al usuario que la llama.
Organizarás tu código en dos archivos:
greetings.rs: Define un trait públicoGreetcon un métodogreetque tome&selfy devuelva unString. Después, crea una estructuraCard(¡no necesita ser pública!) con un campomessage(String). Implementa el traitGreetparaCard, devolviendo el mensaje. Por último, crea una función pública llamadacreate_greetingque tome un parámetroStringy devuelvaimpl Greet. Esta función debe crear y devolver unCardcon el mensaje proporcionado.main.rs: Incluye tu módulo greetings y usa la entrada proporcionada para llamar acreate_greeting. Lo interesante aquí es que tu archivo principal no conoce el tipoCarden absoluto. Solo sabe que recibió algo que implementaGreet. Llama al métodogreetsobre el valor devuelto e imprime el resultado.
La idea clave es que Card permanece privado dentro del módulo, pero quienes llaman a la función aún pueden usarlo a través de la interfaz del trait. La función promete una capacidad (saludar), no un tipo específico.
Tu salida debe mostrar el saludo:
{message}Por ejemplo, con la entrada Happy Birthday!:
Happy Birthday!Y con la entrada Congratulations on your promotion!:
Congratulations on your promotion!Recibirás una entrada: el mensaje de saludo.
Pruébalo tú mismo
mod greetings;
use greetings::Greet;
fn main() {
// Lee el mensaje de saludo desde la entrada
let mut input = String::new();
std::io::stdin().read_line(&mut input).expect("Failed to read line");
let message = input.trim().to_string();
// TODO: Llama a create_greeting con el mensaje
// Note: No conoces el tipo concreto - ¡solo que implementa Greet!
// TODO: Llama al método greet e imprime el resultado
}
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