Traits
En esta página
Un trait declara un conjunto de firmas que cualquier tipo puede satisfacer. La definición no incluye datos — solo la interfaz. Varios traits independientes describen comportamientos ortogonales:
Displayable y Shape son dos traits; Square implementa ambos con cuerpos separados.
// Feature: Trait definition — shared interface
// Syntax: `trait Name { fn method(self) -> Ret /* no body */ }`
// When to use: describe a set of methods that multiple types will
// implement (ad-hoc polymorphism).
// Trait without defaults — only signatures. Each implementer is
// required to provide a body for `display`.
trait Displayable {
fn display(self) -> str
}
// Trait with multiple signatures.
trait Shape {
fn area(self) -> float
fn name(self) -> str
}
struct Square {
side: float,
}
impl Displayable for Square {
fn display(self) -> str {
return "Square({self.side})"
}
}
impl Shape for Square {
fn area(self) -> float {
return self.side * self.side
}
fn name(self) -> str {
return "square"
}
}
let s = Square { side: 4.0 }
print(s.display()) // Square(4)
print(s.area()) // 16
print(s.name()) // square
// expected:
// Square(4)
// 16
// square
impl Trait for Type proporciona el cuerpo de cada método exigido por el trait.
Tipos distintos tienen implementaciones independientes — cada uno resuelve la
misma interfaz a su manera:
Circle y Rectangle implementan HasArea de forma independiente.
// Feature: Trait implementation — `impl Trait for Type`
// Syntax: `impl Trait for Type { fn method(self) { ... } }`
// When to use: declare that a concrete type satisfies a trait by
// providing the body for each required method.
trait HasArea {
fn area(self) -> float
}
struct Circle {
radius: float,
}
struct Rectangle {
width: float,
height: float,
}
// Each `impl Trait for Type` is independent.
impl HasArea for Circle {
fn area(self) -> float {
return 3.14159 * self.radius * self.radius
}
}
impl HasArea for Rectangle {
fn area(self) -> float {
return self.width * self.height
}
}
let c = Circle { radius: 5.0 }
let r = Rectangle { width: 3.0, height: 4.0 }
// The `area` method resolves correctly for each type.
print(c.area()) // 78.53975
print(r.area()) // 12
// expected:
// 78.53975
// 12
Un trait puede incluir métodos por defecto — con cuerpo ya definido en la declaración del trait. El implementador puede aceptar el valor por defecto o sobreescribirlo:
Person usa el comportamiento por defecto de hello; Pet lo reemplaza por un saludo personalizado.
// Feature: Default implementation in traits
// Syntax: inside the `trait`, the fn already has a body `{ ... }`
// When to use: provide default behavior derived from other trait
// methods. Implementers can accept the default or override it in
// `impl Trait for Type`.
//
// Note: the checker resolves methods by looking at the type's
// `impl`. To use the default, we still copy the delegated call —
// that is, `impl` must list ALL trait methods. Here we show
// override and explicit delegation.
trait Greet {
fn name(self) -> str
// Default — implementers MAY redefine.
fn hello(self) -> str {
return "Hello, {self.name()}!"
}
}
struct Person {
full: str,
}
// Accepts the default — copies the body in the impl.
impl Greet for Person {
fn name(self) -> str {
return self.full
}
fn hello(self) -> str {
return "Hello, {self.name()}!"
}
}
struct Pet {
nickname: str,
}
// Overrides the default with something specific.
impl Greet for Pet {
fn name(self) -> str {
return self.nickname
}
fn hello(self) -> str {
return "Hi {self.nickname}, who's a good one?"
}
}
let alice = Person { full: "Alice" }
let rex = Pet { nickname: "Rex" }
print(alice.hello()) // Hello, Alice!
print(rex.hello()) // Hi Rex, who's a good one?
// expected:
// Hello, Alice!
// Hi Rex, who's a good one?
Un mismo tipo puede satisfacer múltiples traits al mismo tiempo, cada uno en
su propio bloque impl Trait for Type. Los métodos de todos los traits quedan
disponibles en la instancia:
Circle implementa HasArea, Named y Describable — describe combina los otros dos.
// Feature: Multiple traits on the same type
// Syntax: several independent `impl Trait for Type`
// When to use: combine orthogonal behaviors — the type satisfies
// each trait separately.
trait HasArea {
fn area(self) -> float
}
trait Named {
fn name(self) -> str
}
trait Describable {
fn describe(self) -> str
}
struct Circle {
radius: float,
}
impl HasArea for Circle {
fn area(self) -> float {
return 3.14159 * self.radius * self.radius
}
}
impl Named for Circle {
fn name(self) -> str {
return "circle"
}
}
impl Describable for Circle {
fn describe(self) -> str {
return "{self.name()} of area {self.area()}"
}
}
let c = Circle { radius: 2.0 }
print(c.area()) // 12.56636
print(c.name()) // circle
print(c.describe()) // circle of area 12.56636
Desafío
Crea un trait Resizable con un método scale(self, factor: float) -> Self.
Impleméntalo para Circle y Rectangle de los ejemplos anteriores.
Consulta también