Los traits definen un conjunto de métodos que un tipo puede implementar. Son el mecanismo de Rust para el polimorfismo: en lugar de herencia, compartes comportamiento implementando traits. Si conoces las interfaces de Java o TypeScript, los traits son conceptualmente similares, aunque con diferencias importantes.
Definir un trait
trait Resumen {
fn resumir(&self) -> String;
}
Solo defines la firma. Cada tipo que implemente Resumen debe proporcionar su propia versión de resumir.
Implementar un trait
struct Articulo {
titulo: String,
autor: String,
contenido: String,
}
impl Resumen for Articulo {
fn resumir(&self) -> String {
format!("{}, por {}", self.titulo, self.autor)
}
}
struct Tweet {
usuario: String,
texto: String,
}
impl Resumen for Tweet {
fn resumir(&self) -> String {
format!("{}: {}", self.usuario, self.texto)
}
}
fn main() {
let art = Articulo {
titulo: String::from("Rust 2026"),
autor: String::from("Ana"),
contenido: String::from("..."),
};
let tweet = Tweet {
usuario: String::from("@ana"),
texto: String::from("Aprendiendo Rust"),
};
println!("{}", art.resumir());
println!("{}", tweet.resumir());
}
Implementaciones por defecto
Los traits pueden tener implementaciones por defecto que los tipos pueden usar o sobreescribir:
trait Resumen {
fn autor(&self) -> String;
// Implementación por defecto que usa autor()
fn resumir(&self) -> String {
format!("(Leer más de {}...)", self.autor())
}
}
struct Tweet {
usuario: String,
texto: String,
}
impl Resumen for Tweet {
fn autor(&self) -> String {
format!("@{}", self.usuario)
}
// resumir() usa la implementación por defecto
}
fn main() {
let t = Tweet { usuario: String::from("ana"), texto: String::from("hola") };
println!("{}", t.resumir()); // (Leer más de @ana...)
}
Traits como parámetros: impl Trait
fn notificar(item: &impl Resumen) {
println!("Notificación: {}", item.resumir());
}
fn main() {
let art = Articulo { /* ... */ };
notificar(&art);
}
impl Trait en la posición de parámetro significa "cualquier tipo que implemente este trait". El compilador genera código especializado para cada tipo (monomorfización).
El orphan rule
Solo puedes implementar un trait para un tipo si el trait o el tipo están definidos en tu crate. No puedes implementar Display de la stdlib para Vec de la stdlib: ambos son externos.
// OK: el trait (Resumen) es tuyo
impl Resumen for Vec<String> { /* ... */ }
// OK: el tipo (MiStruct) es tuyo
impl std::fmt::Display for MiStruct { /* ... */ }
// ERROR: ambos son de la stdlib
// impl std::fmt::Display for Vec<String> { /* ... */ }
Esta regla evita conflictos entre crates que implementaran el mismo trait para el mismo tipo.
Traits con generics
trait Convertir<T> {
fn convertir(&self) -> T;
}
struct Celsius(f64);
struct Fahrenheit(f64);
impl Convertir<Fahrenheit> for Celsius {
fn convertir(&self) -> Fahrenheit {
Fahrenheit(self.0 * 9.0 / 5.0 + 32.0)
}
}
fn main() {
let c = Celsius(100.0);
let f: Fahrenheit = c.convertir();
println!("{}°F", f.0); // 212°F
}
Resumen
- Un trait define un conjunto de métodos que un tipo debe implementar.
- Puedes tener implementaciones por defecto para métodos no críticos.
impl Traiten parámetros acepta cualquier tipo que implemente ese trait.- El orphan rule impide implementar traits externos para tipos externos.
- Los traits son el mecanismo central de polimorfismo en Rust, sin herencia.
El siguiente artículo cubre los trait bounds: cómo restringir los tipos que acepta una función genérica, la cláusula where, impl Trait frente a T: Trait y las blanket implementations.
