Trait
- Puede considerarse similar a un "contrato" en el sentido de que define un conjunto de métodos que un tipo debe implementar
- Un trait es similar a una interfaz en otros lenguajes de programación, como Java o C#
- Los traits en Rust permiten el polimorfismo, lo que significa que se pueda escribir funciones o estructuras genéricas que operen sobre cualquier tipo que implemente un trait específico.
- Un tipo que implemente un trait debe proporcionar definiciones para los métodos que el trait requiere, a menos que el trait ya provea implementaciones por defecto para algunos de sus métodos.
- Un trait permite definir un comportamiento que puede ser compartido por diferentes tipos. Por ejemplo, si varios tipos pueden ser "mostrados" (impresos), se puede definir un trait que establezca cómo deben implementar este comportamiento (debug).
- En arquitecturas como DDD, donde se definen interfaces del dominio (
domain interface) para representar contratos o comportamientos comunes en un sistema, los traits pueden servir como esa interfaz o contrato en Rust. (Los traits son una construcción del lenguaje, mientras que "domain" es un concepto de diseño de software)
Ejemplo. Operaciones bancarias comunes
- Diseñando un sistema para manejar operaciones bancarias en el contexto de DDD.
- Se podría definir un trait como la interfaz del dominio que representa operaciones bancarias comunes, como
Depositar,Retirar, etc.
Definir trait
- Se define un trait que actúa como la interfaz del dominio para una cuenta bancaria
- Trait
OperacionesBancariasdefine la "interfaz" o "contrato" del dominio para cualquier tipo que represente una cuenta bancaria. Este trait exige que cualquier cuenta implemente los métodosdepositar,retiraryobtener_balance.
trait OperacionesBancarias {
fn depositar(&mut self, monto: f64);
fn retirar(&mut self, monto: f64) -> Result<f64, String>;
fn obtener_balance(&self) -> f64;
}
Implementar
- Implementaciones específicas:
CuentaAhorrosyCuentaCorrienterepresentan dos tipos de cuentas en el dominio. Ambas implementan el contrato definido porOperacionesBancarias, pero lo hacen de manera que refleje sus diferencias de comportamiento.
// Implementación para una cuenta de ahorros ###################
struct CuentaAhorros {
balance: f64,
}
impl OperacionesBancarias for CuentaAhorros {
fn depositar(&mut self, monto: f64) {
self.balance += monto;
}
fn retirar(&mut self, monto: f64) -> Result<f64, String> {
if self.balance >= monto {
self.balance -= monto;
Ok(monto)
} else {
Err("Fondos insuficientes".to_string())
}
}
fn obtener_balance(&self) -> f64 {
self.balance
}
}
// Implementación para una cuenta corriente ####################
struct CuentaCorriente {
balance: f64,
limite_credito: f64,
}
impl OperacionesBancarias for CuentaCorriente {
fn depositar(&mut self, monto: f64) {
self.balance += monto;
}
fn retirar(&mut self, monto: f64) -> Result<f64, String> {
if self.balance + self.limite_credito >= monto {
self.balance -= monto;
Ok(monto)
} else {
Err("Fondos insuficientes, incluso con crédito".to_string())
}
}
fn obtener_balance(&self) -> f64 {
self.balance
}
}
Uso
fn main() {
let mut ahorros = CuentaAhorros { balance: 1000.0 };
let mut corriente = CuentaCorriente {
balance: 500.0,
limite_credito: 1000.0
};
ahorros.depositar(200.0);
println!("Balance en ahorros: ${}", ahorros.obtener_balance());
match corriente.retirar(1200.0) {
Ok(_) => println!("Retiro exitoso"),
Err(e) => println!("Error: {}", e),
}
println!("Balance en corriente: ${}", corriente.obtener_balance());
}
Ejemplo. Sistema de Pagos
Traits y estructuras
- Trait
ProcesadorDePago: Define un métodoprocesar_pagoque todos los métodos de pago deben implementar. - Este método toma un monto como argumento y retorna un booleano indicando si el pago fue exitoso.
// Trait para procesar pagos
trait ProcesadorDePago {
fn procesar_pago(&self, monto: f64) -> bool;
}
// Estructura para pagos con tarjeta de crédito
struct TarjetaCredito {
numero: String,
nombre_titular: String,
vencimiento: String,
}
// Estructura para pagos con PayPal
struct PayPal {
cuenta_email: String,
}
// Estructura para pagos con criptomonedas
struct CriptoMoneda {
direccion_wallet: String,
}
Implementación de los traits
Implementaciones específicas:
- TarjetaCredito: Procesa pagos con tarjeta de crédito.
- PayPal: Procesa pagos a través de PayPal.
- CriptoMoneda: Procesa pagos en criptomonedas.
Función procesar:
Esta función toma cualquier tipo que implemente el trait ProcesadorDePago y procesa un pago. Esto demuestra cómo se puede utilizar polimorfismo con traits en Rust.
// Implementación del trait para pagos con tarjeta de crédito
impl ProcesadorDePago for TarjetaCredito {
fn procesar_pago(&self, monto: f64) -> bool {
println!(
"Procesando pago de ${} con tarjeta de crédito a nombre de {}",
monto, self.nombre_titular
);
// Aquí iría la lógica real de procesamiento del pago
true // Simulamos que el pago fue exitoso
}
}
// Implementación del trait para pagos con PayPal
impl ProcesadorDePago for PayPal {
fn procesar_pago(&self, monto: f64) -> bool {
println!(
"Procesando pago de ${} a través de PayPal con la cuenta {}",
monto, self.cuenta_email
);
// Aquí iría la lógica real de procesamiento del pago
true // Simulamos que el pago fue exitoso
}
}
// Implementación del trait para pagos con criptomonedas
impl ProcesadorDePago for CriptoMoneda {
fn procesar_pago(&self, monto: f64) -> bool {
println!(
"Procesando pago de ${} en criptomoneda a la dirección {}",
monto, self.direccion_wallet
);
// Aquí iría la lógica real de procesamiento del pago
true // Simulamos que el pago fue exitoso
}
}
// Función que acepta cualquier tipo de procesador de pago
fn procesar<T: ProcesadorDePago>(procesador: T, monto: f64) {
if procesador.procesar_pago(monto) {
println!("Pago procesado exitosamente.");
} else {
println!("Hubo un error al procesar el pago.");
}
}
Uso
fn main() {
let tarjeta = TarjetaCredito {
numero: "1234-5678-9012-3456".to_string(),
nombre_titular: "Juan Pérez".to_string(),
vencimiento: "12/24".to_string(),
};
let paypal = PayPal {
cuenta_email: "[email protected]".to_string(),
};
let cripto = CriptoMoneda {
direccion_wallet: "1A2b3C4d5E6f7G8H9I0J".to_string(),
};
// Procesamos diferentes tipos de pagos
procesar(tarjeta, 100.0);
procesar(paypal, 200.0);
procesar(cripto, 300.0);
}