Rustelo/info/about_enum_trait_dyn.md

98 lines
5.2 KiB
Markdown
Raw Normal View History

### Problema: Trait no "dyn compatible" en Rust
En Rust, un trait no es "dyn compatible" (antes llamado "object safe") si:
- Tiene tipos asociados.
- Tiene métodos genéricos.
Esto impide usar trait objects como `Box<dyn DatabaseConnection>`, ya que Rust necesita saber cómo despachar métodos en tiempo de ejecución y los tipos asociados o métodos genéricos lo hacen imposible[1][2].
### Solución: Uso de Enum en lugar de Trait Object
Cuando un trait no es "dyn compatible", una alternativa común es usar un **enum** que encapsule todas las implementaciones concretas que necesitas manejar. Así evitas las restricciones de los trait objects y puedes seguir usando polimorfismo, pero de manera estática.
#### Ejemplo
Supón que tienes dos structs que implementan `DatabaseConnection`:
```rust
struct PostgresConnection { /* ... */ }
struct SqliteConnection { /* ... */ }
```
En lugar de:
```rust
trait DatabaseConnection {
type Row;
fn query<T>(&self, sql: &str) -> T; // método genérico, no object-safe
}
```
Define un enum:
```rust
enum DatabaseConnectionEnum {
Postgres(PostgresConnection),
Sqlite(SqliteConnection),
}
```
Y luego implementa métodos para el enum que deleguen a cada variante:
```rust
impl DatabaseConnectionEnum {
fn query<T>(&self, sql: &str) -> T {
match self {
DatabaseConnectionEnum::Postgres(conn) => conn.query(sql),
DatabaseConnectionEnum::Sqlite(conn) => conn.query(sql),
}
}
}
```
### Ventajas y Desventajas
| Opción | Ventajas | Desventajas |
|-------------------|-------------------------------------------------|------------------------------------|
| Enum | Más rápido (dispatch estático), sin restricciones de object safety[3][4] | Solo puedes manejar variantes conocidas en tiempo de compilación |
| Trait Object (`dyn`) | Extensible, permite tipos externos | Más lento (dispatch dinámico), requiere trait object-safe |
### Cuándo usar Enum
- Cuando conoces todas las implementaciones posibles en tiempo de compilación.
- Cuando necesitas evitar las restricciones de object safety.
- Cuando el rendimiento es crítico y quieres evitar el coste de la vtable[3][5].
### Resumen
- Un trait con tipos asociados o métodos genéricos no puede usarse como trait object.
- Refactoriza usando un enum que contenga todas las implementaciones concretas.
- Implementa métodos en el enum que deleguen a cada variante.
- Elige enum si el conjunto de tipos es cerrado y controlado por ti.
Esta técnica es común en Rust para evitar los límites de los trait objects y seguir obteniendo polimorfismo estático y eficiente[3][6][5].
Sources
[1] Traits - The Rust Reference https://doc.rust-lang.org/reference/items/traits.html
[2] Why doesn't Rust support trait objects with associated constants? https://stackoverflow.com/questions/77433184/why-doesnt-rust-support-trait-objects-with-associated-constants
[3] Enum or Trait Object - Possible Rust https://www.possiblerust.com/guide/enum-or-trait-object
[4] Trait Object or Enum, how to choice - Rust Users Forum https://users.rust-lang.org/t/trait-object-or-enum-how-to-choice/100268
[5] Should I use enums or boxed trait objects to emulate polymorphism? https://stackoverflow.com/questions/52240099/should-i-use-enums-or-boxed-trait-objects-to-emulate-polymorphism
[6] Polymorphism in Rust: Enums vs Traits - Matthew Kennedy https://www.mattkennedy.io/blog/rust_polymorphism/
[7] dyn https://doc.rust-lang.org/std/keyword.dyn.html
[8] Returning Traits with dyn - Rust By Example https://doc.rust-lang.org/rust-by-example/trait/dyn.html
[9] "object safety" is now called "dyn compatibility" : r/rust https://www.reddit.com/r/rust/comments/1i0hwa5/unmentioned_1840_change_object_safety_is_now/
[10] Dyn async traits, part 10: Box box box · baby steps https://smallcultfollowing.com/babysteps/blog/2025/03/24/box-box-box/
[11] Pre-RFC: Object-safe traits with associated types - Rust Internals https://internals.rust-lang.org/t/pre-rfc-object-safe-traits-with-associated-types/7996
[12] trait not dyn compatible when using dyn Trait in ... https://github.com/rust-lang/rust/issues/136744
[13] Trait Objects - The Rust Programming Language - MIT https://web.mit.edu/rust-lang_v1.25/arch/amd64_ubuntu1404/share/doc/rust/html/book/first-edition/trait-objects.html
[14] A tour of `dyn Trait` - tutorials https://users.rust-lang.org/t/a-tour-of-dyn-trait/97053
[15] dyn Trait implementations - Learning Rust https://quinedot.github.io/rust-learning/dyn-trait-impls.html
[16] Trait objects do not work with generic associated types #81823 https://github.com/rust-lang/rust/issues/81823
[17] Why is `Sized` trait not allowed in dyn MyTrait? https://users.rust-lang.org/t/why-is-sized-trait-not-allowed-in-dyn-mytrait/125323
[18] Understanding trait object safety - return types - Rust Users Forum https://users.rust-lang.org/t/understanding-trait-object-safety-return-types/73425
[19] Enum or Trait Object : r/rust - Reddit https://www.reddit.com/r/rust/comments/hz5iwm/enum_or_trait_object/
[20] Any alternatives to dyn when your trait is not object-safe? https://stackoverflow.com/questions/77582616/any-alternatives-to-dyn-when-your-trait-is-not-object-safe