Error E0790 de Rust: cannot call associated function on trait (falta el tipo)
E0790 significa que le pediste a un trait algo que solo puede darte un tipo. Default::default() a secas es como decir “dame el valor por defecto” sin decir el valor por defecto de qué. Es un error común, y la solución suele ser poner delante el nombre del tipo: Settings::default() en lugar de Default::default(). Esta página te muestra primero las soluciones y después explica la diferencia entre un trait y un tipo, para que este error deje de sorprenderte. Forma parte de la guía de errores del compilador.
El mensaje de error
Sección titulada «El mensaje de error»error[E0790]: cannot call associated function on trait without specifying the corresponding `impl` type --> src/main.rs:8:20 |8 | let settings = Default::default(); // ❌ ¿el valor por defecto de qué tipo? | ^^^^^^^^^^^^^^^^^^ cannot call associated function of trait |help: use a fully-qualified path to a specific available implementation |8 | let settings = </* self type */ as Default>::default(); // ❌ ¿el valor por defecto de qué tipo? | +++++++++++++++++++ +El mensaje quiere decir “no se puede llamar a una función asociada de un trait sin indicar el tipo del impl correspondiente”. El help propone usar una ruta completa (fully-qualified path) hacia una implementación concreta.
Este es el programa que lo produce:
#[derive(Debug, Default)]struct Settings { volume: u8, dark_mode: bool,}
fn main() { let settings = Default::default(); // ❌ ¿el valor por defecto de qué tipo? println!("{:?}", settings);}Te sale el mismo error cuando llamas a una función de tu propio trait a través del nombre del trait, como Shape::new(). Aquí rustc ve dos tipos que implementan Shape, así que te ofrece los dos:
error[E0790]: cannot call associated function on trait without specifying the corresponding `impl` type --> src/main.rs:33:17 | 2 | fn new() -> Self; | ----------------- `Shape::new` defined here...33 | let shape = Shape::new(); | ^^^^^^^^^^^^ cannot call associated function of trait |help: use a fully-qualified path to one of the available implementations |33 | let shape = <Circle as Shape>::new(); | ++++++++++ +33 | let shape = <Square as Shape>::new(); | ++++++++++ +El help sugiere una de las implementaciones disponibles: la de Circle o la de Square.
Con una constante de un trait, como Plugin::ID, el titular cambia un poco: cannot refer to the associated constant on trait without specifying the corresponding `impl` type (no se puede usar la constante asociada de un trait sin indicar el tipo). Mismo problema, mismas soluciones.
Qué significa
Sección titulada «Qué significa»Fíjate en lo que marca rustc:
^^^^debajo deDefault::default(): “cannot call associated function of trait” (no se puede llamar a la función asociada del trait).defaultes una función que pertenece al traitDefault, y la llamaste a través del nombre del trait.- El
helpsugiere</* self type */ as Default>::default(). La parte/* self type */es un hueco que tienes que rellenar tú. rustc te pregunta: “¿eldefaultde qué tipo quieres decir?”. - En el ejemplo de
Shape, rustc rellena el hueco por ti con cada tipo que encontró (Circle,Square). Si solo un tipo implementa el trait, sugiere únicamente ese.
Esta es la idea clave. Un trait es una promesa: “cualquier tipo que implemente Default sabe crear un valor inicial de sí mismo”. Un tipo es quien cumple la promesa: Settings, String, u32 y Vec<T> la cumplen cada uno a su manera. Default::default() le pide un valor a la propia promesa, pero una promesa no puede fabricar nada. Solo un tipo puede.
Piensa en un cartel de “comida para llevar”. Muchos restaurantes lo ponen en su ventana. Pero no puedes pedirle la cena al cartel; tienes que elegir un restaurante. Settings::default() es elegir el restaurante.
Por qué Rust hace esto
Sección titulada «Por qué Rust hace esto»La mayoría de los métodos de un trait reciben self, como circle.area(). Esos nunca provocan E0790, porque el valor le dice a Rust el tipo: circle es un Circle, así que llama al area de Circle. Puedes leer más sobre esto en Traits.
Algunas funciones de un trait no tienen self: constructores como new() y default(), conversiones como From::from y constantes del trait como ID. Sin un valor que mirar, Rust solo tiene dos sitios donde averiguar el tipo: el nombre que escribes delante (Settings::) o el tipo que debe tener el resultado (let s: Settings = ...). Si no hay ninguno de los dos, Rust no puede adivinarlo. Hay muchísimos tipos que implementan Default, y escoger uno al azar cambiaría sin avisar lo que hace tu programa.
Es lo mismo que una interfaz en Java o C#. No puedes hacer new de una interfaz; necesitas una clase que la implemente. Rust simplemente te lo dice al compilar, y te señala el sitio exacto que hay que arreglar.
Cómo solucionarlo
Sección titulada «Cómo solucionarlo»Solución 1: Llámala sobre el tipo
Sección titulada «Solución 1: Llámala sobre el tipo»Elige esta casi siempre. Cambia el nombre del trait por el nombre del tipo. Es la solución más corta, y se lee bien: “el Settings por defecto”, “un Circle nuevo”:
#[derive(Debug, Default)]struct Settings { volume: u8, dark_mode: bool,}
trait Shape { fn new() -> Self; fn name() -> String;}
struct Circle { radius: f64,}
impl Shape for Circle { fn new() -> Self { Circle { radius: 1.0 } } fn name() -> String { String::from("círculo") }}
fn main() { let settings = Settings::default(); // en lugar de Default::default() let shape = Circle::new(); // en lugar de Shape::new()
println!("{:?}", settings); println!("Un {} con radio {}", Circle::name(), shape.radius);}Salida:
Settings { volume: 0, dark_mode: false }Un círculo con radio 1Circle::new() funciona aunque new esté definida en el trait y no en un bloque impl Circle. Rust busca new en Circle, la encuentra a través de impl Shape for Circle y la llama. El trait tiene que estar en el ámbito (scope): definido en el mismo archivo, o importado con use.
Solución 2: Anota el tipo de la variable
Sección titulada «Solución 2: Anota el tipo de la variable»Elige esta cuando te gusta el estilo Default::default(), o cuando el nombre del tipo es largo. Escribe el tipo en la variable, y Rust deduce a partir de él qué default llamar:
#[derive(Debug, Default)]struct Settings { volume: u8, dark_mode: bool,}
fn main() { let settings: Settings = Default::default(); let small: u8 = 42; let big: u32 = From::from(small);
println!("{:?}", settings); println!("{}", big);}Salida:
Settings { volume: 0, dark_mode: false }42Esto solo funciona cuando la función devuelve el propio tipo (Self), como hacen default(), new() y from(). No funciona con fn name() -> String: saber que el resultado es un String no le dice a Rust de qué figura hablas. Para esas, usa la Solución 1 o la Solución 3. Más sobre anotaciones en Tipos de datos.
Solución 3: Sintaxis totalmente calificada <Type as Trait>::function()
Sección titulada «Solución 3: Sintaxis totalmente calificada <Type as Trait>::function()»Esta es la forma de “decirlo todo” que sugiere rustc. Lee <Circle as Shape>::new() como “el new que tiene Circle porque implementa Shape”. Siempre funciona, pero Circle::new() se lee mejor, así que solo la necesitas cuando la forma corta es ambigua:
- el tipo implementa dos traits con una función del mismo nombre, o
- el tipo tiene su propia función con ese nombre, y tú quieres la versión del trait.
Aquí una venta (Sale) se puede escribir como fila de un CSV o como informe impreso, y los dos traits tienen un header(). Escribir Sale::header() da E0034 (en inglés), “multiple applicable items in scope” (varios elementos aplicables en el ámbito), así que dices cuál quieres:
trait Csv { fn header() -> String;}
trait Report { fn header() -> String;}
struct Sale;
impl Csv for Sale { fn header() -> String { String::from("fecha,importe") }}
impl Report for Sale { fn header() -> String { String::from("Ventas del mes") }}
fn main() { println!("{}", <Sale as Csv>::header()); println!("{}", <Sale as Report>::header());}Salida:
fecha,importeVentas del mesLa lección de Traits avanzados tiene otro ejemplo, también con métodos que reciben &self.
Solución 4: En código genérico, usa T:: y el turbofish
Sección titulada «Solución 4: En código genérico, usa T:: y el turbofish»Elige esta cuando estás escribiendo una función genérica. Dentro de fn register<T: Plugin>(), el tipo es T, así que llama a T::name() y T::ID, no a Plugin::name(). Al llamar a la función, pasa el tipo con el turbofish ::<Type>, porque no hay ningún argumento del que Rust pueda deducirlo:
trait Plugin { const ID: u32; fn name() -> String;}
struct Logger;struct Backup;
impl Plugin for Logger { const ID: u32 = 7; fn name() -> String { String::from("logger") }}
impl Plugin for Backup { const ID: u32 = 12; fn name() -> String { String::from("backup") }}
fn register<T: Plugin>() -> String { format!("Plugin n.º {}: {}", T::ID, T::name())}
fn main() { println!("{}", register::<Logger>()); println!("{}", register::<Backup>());}Salida:
Plugin n.º 7: loggerPlugin n.º 12: backupSi te olvidas del turbofish y escribes register(), te sale E0283: “cannot infer type of the type parameter T” (no se puede deducir el tipo del parámetro T). Aquí T también es el tipo, así que T:: funciona igual que Circle:: en la Solución 1. Mira Genéricos para saber más sobre los parámetros de tipo.
Dónde lo verás normalmente
Sección titulada «Dónde lo verás normalmente»Default::default() sin ninguna pista. let config = Default::default(); falla, pero la misma llamada funciona allí donde el tipo ya se conoce: en un campo de un struct, en un argumento de función o en la sintaxis de “rellena el resto”:
let settings = Settings { volume: 8, ..Default::default() }; // OK: Rust sabe que es SettingsArregla la línea que falla con Settings::default() o con let config: Settings = Default::default();. Mira Traits comunes.
Dentro de una función genérica. Escribir el nombre del trait en lugar de T:
error[E0790]: cannot call associated function on trait without specifying the corresponding `impl` type --> src/main.rs:9:29 |2 | fn name() -> String; | -------------------- `Shape::name` defined here...9 | format!("This is a {}", Shape::name()) | ^^^^^^^^^^^^^ cannot call associated function of trait |help: use the fully-qualified path to the only available implementation |9 | format!("This is a {}", <Circle as Shape>::name()) | ++++++++++ +Esta vez el help propone la única implementación disponible. Cuidado: rustc sugiere <Circle as Shape> porque, de momento, Circle es la única figura. Eso compila, pero la función diría siempre “circle”, sea cual sea T. La solución correcta es T::name(), como en la Solución 4.
Constantes de traits. println!("{}", Plugin::ID); le pide un número a la promesa, y solo cada plugin tiene el suyo. Solución: Logger::ID, o T::ID en código genérico.
From::from(x) sin un destino. let total = From::from(42u8); da E0790, porque hay muchos tipos que se pueden crear a partir de un u8. Solución: u32::from(42u8), o let total: u32 = 42u8.into();. Mira Conversiones.
Errores relacionados
Sección titulada «Errores relacionados»- E0282: type annotations needed (hacen falta anotaciones de tipo). Si usas el valor después, como
let c = Shape::new();seguido dec.area(), rustc informa E0282 en su lugar. Sirven las mismas soluciones. - E0283: hacen falta anotaciones de tipo cuando encajan muchos tipos, por ejemplo
Into::into(n)sin destino, o una función genérica llamada sin turbofish. - E0599: no se encuentra la función.
Settings::default()da este error siSettingsno implementaDefault; añade#[derive(Default)]. Conlet s: Settings = Default::default();el mismo despiste aparece como E0277. - E0424:
selfusado en una función sin parámetroself. También trata de funciones que pertenecen a un tipo y no a un valor concreto. - Todos los errores del compilador: la lista completa de páginas de errores.
Comprueba lo que aprendiste
Sección titulada «Comprueba lo que aprendiste»Comprueba lo aprendido
1. let opts = Default::default(); da E0790. Options deriva Default. ¿Qué línea lo arregla?
2. ¿Por qué let s: Settings = Default::default(); compila, pero let n: String = Shape::name(); sigue dando E0790?
3. Dentro de fn show<T: Shape>(), ¿cómo deberías llamar a la función name() del trait?
Puntuación: 0 / 3
Para leer más
Sección titulada «Para leer más»- E0790 — Rust error codes index (en inglés): la explicación oficial de este error, con un ejemplo corto y su solución.
- Traits — El libro de Rust (en inglés): qué es un trait, cómo implementarlo y las implementaciones por defecto. También existe una traducción al español de la comunidad del libro.
- Advanced Traits — El libro de Rust (en inglés): incluye la sección sobre la sintaxis totalmente calificada, con el clásico ejemplo
<Dog as Animal>::baby_name(). - Disambiguating overlapping traits — Rust by Example (en inglés): un ejemplo corto y ejecutable para elegir entre dos traits que tienen un método con el mismo nombre.
Default— documentación de la biblioteca estándar (en inglés): cómo derivarDefault, implementarlo a mano y el patrón..Default::default().