Implementaciones por defecto
Parte de la sección Programación Orientada a Objetos del Journey de Rust de Coddy. Lección 31 de 61.
Hasta ahora, cada método de rasgo que hemos definido ha sido solo una firma: un requisito sin cuerpo. Pero ¿qué ocurre si la mayoría de los tipos que implementan un rasgo usarían la misma lógica? Rust te permite proporcionar una implementación predeterminada directamente en la definición del rasgo.
En lugar de terminar la firma del método con un punto y coma, añades un cuerpo:
trait Greet {
fn greet(&self) -> String {
String::from("Hello there!")
}
}
Ahora cualquier tipo que implemente Greet obtiene automáticamente este método, sin escribir ningún código en el bloque impl:
struct Guest;
impl Greet for Guest {}
let visitor = Guest;
println!("{}", visitor.greet()); // ¡Hola!
El impl Greet for Guest {} vacío es válido porque el trait ya proporciona el comportamiento. La estructura simplemente «opta por» el trait y recibe el método predeterminado de forma gratuita.
Este patrón es útil cuando quieres definir un comportamiento compartido que funcione en la mayoría de los casos. Los tipos que necesitan algo diferente pueden sobrescribir el valor predeterminado (lo cual aprenderás a continuación), mientras que los tipos satisfechos con el comportamiento estándar no necesitan repetirse.
Desafío
Fácil¡Exploremos el poder de las implementaciones predeterminadas creando un sistema de notificaciones! Crearás un trait que proporciona un comportamiento estándar desde el principio, para que los tipos puedan optar por él sin escribir código repetitivo.
Organizarás tu código en tres archivos:
notifiable.rs: Define un trait públicoNotifiablecon un métodonotify(&self) -> Stringque tiene una implementación predeterminada. La implementación predeterminada debe devolver"You have a new notification!". Cualquier tipo que implemente este trait recibirá automáticamente este comportamiento sin necesidad de escribir nada en su bloque impl.alerts.rs: Crea dos estructuras unitarias públicas sencillas que implementen tu trait:EmailAlert: implementaNotifiablecon un bloque impl vacío (usa la implementación predeterminada)SystemAlert: también implementaNotifiablecon un bloque impl vacío (usa la implementación predeterminada)
main.rs: Reúne tus módulos y demuestra cómo ambos tipos de alerta comparten el mismo comportamiento predeterminado. Crea instancias de ambas estructuras y llama a sus métodosnotify.
En tu archivo principal, crea un EmailAlert y un SystemAlert, y después imprime sus notificaciones en líneas separadas.
Tu salida debe ser:
You have a new notification!
You have a new notification!Observa cómo ambos tipos producen una salida idéntica: ¡esa es la belleza de las implementaciones predeterminadas! Ninguna de las dos estructuras necesitó escribir código para el método notify, pero ambas tienen un comportamiento de notificación completamente funcional. En la próxima lección, aprenderás a sobrescribir estas implementaciones predeterminadas cuando un tipo necesite un comportamiento personalizado.
Pruébalo tú mismo
mod notifiable;
mod alerts;
use notifiable::Notifiable;
use alerts::{EmailAlert, SystemAlert};
fn main() {
// TODO: Crea una instancia de EmailAlert
// TODO: Crea una instancia de SystemAlert
// TODO: Print the notification from EmailAlert
// TODO: Print the notification from SystemAlert
}
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