Los traits pueden implementar el comportamiento en términos de otros métodos de traits:
trait Equals {
fn equals(&self, other: &Self) -> bool;
fn not_equals(&self, other: &Self) -> bool {
!self.equals(other)
}
}
#[derive(Debug)]
struct Centimeter(i16);
impl Equals for Centimeter {
fn equals(&self, other: &Centimeter) -> bool {
self.0 == other.0
}
}
fn main() {
let a = Centimeter(10);
let b = Centimeter(20);
println!("{a:?} equals {b:?}: {}", a.equals(&b));
println!("{a:?} not_equals {b:?}: {}", a.not_equals(&b));
}
Los traits pueden especificar métodos preimplementados (predeterminados) y métodos que los usuarios deben implementar ellos mismos. Los métodos con implementaciones predeterminadas pueden depender de los métodos requeridos. Por ejemplo podemos implementar un trait NotEquals y mover el método not_equals a este y hacer de Equals un súper rasgo para NotEquals.
trait NotEquals: Equals {
fn not_equals(&self, other: &Self) -> bool {
!self.equals(other)
}
}
O podemos proporcionar una implementación general de NotEquals, y especificar una implementación para todos los que implementen Equals.
trait NotEquals {
fn not_equals(&self, other: &Self) -> bool;
}
impl<T> NotEquals for T where T: Equals {
fn not_equals(&self, other: &Self) -> bool {
!self.equals(other)
}
}
Con la implementación general, ya no necesita a Equals como un trait general para NotEqual.