Error E0424 de Rust: expected value, found module `self` (sin parámetro self)
E0424 significa que escribiste self dentro de una función que no tiene un parámetro self, así que no hay ningún “self” al que Rust pueda referirse. Es un despiste muy común, sobre todo si vienes de Java, C# o JavaScript, donde this siempre está ahí. La solución suele ser una sola palabra: añadir &self a la lista de parámetros de la función. Esta página forma parte de la guía de errores del compilador.
El mensaje de error
Sección titulada «El mensaje de error»error[E0424]: expected value, found module `self` --> src/main.rs:7:32 |6 | fn describe() { | -------- this function doesn't have a `self` parameter7 | println!("Player: {}", self.name); // ❌ ¿qué jugador? | ^^^^ `self` value is a keyword only available in methods with a `self` parameter |help: add a `self` receiver parameter to make the associated `fn` a method |6 | fn describe(&self) { | +++++
error[E0599]: no method named `describe` found for struct `Player` in the current scope --> src/main.rs:13:10 |13 | hero.describe(); | ^^^^^^^^ this is an associated function, not a method“Expected value, found module self” quiere decir “se esperaba un valor, pero se encontró el módulo self”. El help propone añadir un parámetro &self para que la función asociada se convierta en un método.
Este es el programa que lo produce:
struct Player { name: String,}
impl Player { fn describe() { println!("Player: {}", self.name); // ❌ ¿qué jugador? }}
fn main() { let hero = Player { name: String::from("Ferris") }; hero.describe();}El segundo error, E0599 (“no method named describe found”, no se encontró el método), es una consecuencia del primero. Arregla E0424 y desaparece también.
Qué significa
Sección titulada «Qué significa»Mira las dos etiquetas que rustc pone en tu código:
- Línea 6:
fn describe()tiene los paréntesis vacíos. rustc te avisa: “this function doesn’t have aselfparameter” (esta función no tiene un parámetroself). - Línea 7:
self.namepide el nombre de “el jugador sobre el que se llamó la función”. Perodescribeno recibió ningún jugador, así que aquíselfno significa nada.
Una función dentro de un bloque impl sin parámetro self es una función asociada (associated function). Pertenece al tipo (Player::describe()), no a un valor concreto. Una función con parámetro self es un método, y solo los métodos pueden usar self.
Piensa en un formulario que dice “escribe aquí tu propio nombre”. Solo tiene sentido cuando alguien lo tiene en la mano. describe() es un formulario que nadie está sujetando.
¿Por qué dice “found module self” (se encontró el módulo self)? En Rust, self tiene un segundo significado: el módulo actual, como en use self::helpers;. Cuando no hay un valor self, rustc recurre a ese significado y luego se queja de que un módulo no es un valor. Puedes ignorar la palabra “module”; el problema real es el parámetro que falta.
Por qué Rust hace esto
Sección titulada «Por qué Rust hace esto»En Rust no hay nada oculto. Java y C# dan a cada método no estático un this invisible. Rust te pide que escribas self como primer parámetro, y además que digas cómo lo recibe el método: solo para mirarlo, para cambiarlo o quedándose con el ownership (propiedad). Esa elección forma parte de las reglas del borrowing (préstamo), así que tiene que verse.
Así, cualquiera que lea tu código (y también el compilador) sabe exactamente qué hace un método con su valor. fn summary(&self) promete no cambiar nada. fn add(&mut self, ...) avisa de que cambia cosas. fn checkout(self) consume el valor. Después, el compilador comprueba esas promesas por ti. Los métodos y los bloques impl se explican en Structs.
Cómo solucionarlo
Sección titulada «Cómo solucionarlo»Solución 1: Añadir &self, &mut self o self
Sección titulada «Solución 1: Añadir &self, &mut self o self»Elige esta cuando la función debe trabajar sobre un valor que ya existe. Es la solución correcta casi siempre. Añade el parámetro que sugiere rustc y llama al método con un punto:
struct Player { name: String,}
impl Player { fn describe(&self) { println!("Player: {}", self.name); }}
fn main() { let hero = Player { name: String::from("Ferris") }; hero.describe();}Salida:
Player: Ferrisrustc siempre sugiere &self, pero hay tres variantes. Elige la que encaje con lo que hace el método:
| Parámetro | El método… | Nombres típicos |
|---|---|---|
&self |
solo lee el valor | summary, len, is_empty, describe |
&mut self |
cambia el valor | add, push, reset, level_up |
self |
consume el valor (se queda con el ownership) | into_..., finish, build, checkout |
Aquí tienes las tres en un mismo tipo:
struct Order { items: Vec<String>,}
impl Order { fn summary(&self) -> String { format!("{} artículo(s): {}", self.items.len(), self.items.join(", ")) }
fn add(&mut self, item: &str) { self.items.push(item.to_string()); }
fn checkout(self) -> Vec<String> { self.items }}
fn main() { let mut order = Order { items: Vec::new() }; order.add("café"); order.add("tostada"); println!("{}", order.summary()); let packed = order.checkout(); // aquí order se consume println!("Empaquetado: {:?}", packed);}Salida:
2 artículo(s): café, tostadaEmpaquetado: ["café", "tostada"]Si eliges &self pero el método cambia un campo, el compilador te lo dice enseguida (E0594, “cannot assign to self.count, which is behind a & reference”, es decir, no se puede asignar a través de una referencia &; o E0596 para llamadas como push). Cámbialo a &mut self. Si dudas, empieza con &self; self es el caso raro.
Solución 2: Llamarlo como Tipo::método(&valor)
Sección titulada «Solución 2: Llamarlo como Tipo::método(&valor)»Cuando una función ya tiene un parámetro self, puedes llamarla de dos formas. hero.label() y Player::label(&hero) significan exactamente lo mismo: el valor que pondrías antes del punto pasa a ser el primer argumento. La forma con punto es la que escribirás casi siempre. La forma Tipo:: es útil cuando quieres pasar el propio método a algo como map:
struct Player { name: String, level: u32,}
impl Player { fn label(&self) -> String { format!("{} (nivel {})", self.name, self.level) }}
fn main() { let hero = Player { name: String::from("Ferris"), level: 3 }; println!("{}", hero.label()); println!("{}", Player::label(&hero)); // la misma llamada
let team = vec![hero, Player { name: String::from("Corro"), level: 5 }]; let labels: Vec<String> = team.iter().map(Player::label).collect(); println!("{:?}", labels);}Salida:
Ferris (nivel 3)Ferris (nivel 3)["Ferris (nivel 3)", "Corro (nivel 5)"]Lo que no funciona es Player::label() sin ningún valor: un método siempre necesita su self. Si lo omites, obtienes E0061, “this function takes 1 argument but 0 arguments were supplied” (la función recibe 1 argumento, pero se pasaron 0).
Solución 3: Pasar lo que la función necesita
Sección titulada «Solución 3: Pasar lo que la función necesita»Elige esta cuando todavía no existe ningún valor, o cuando la función ni siquiera está en un bloque impl.
El caso clásico es un constructor. new crea el valor, así que no hay ningún self del que leer. Dale a new los datos iniciales como parámetros normales y construye el struct con Self { ... }:
struct Counter { count: u32, step: u32,}
impl Counter { fn new(step: u32) -> Self { Self { count: 0, step } }
fn tick(&mut self) { self.count += self.step; }}
fn main() { let mut counter = Counter::new(5); counter.tick(); counter.tick(); println!("Contador: {}", counter.count);}Salida:
Contador: 10Fíjate en las mayúsculas: Self (con S mayúscula) es el tipo, así que Self { ... } construye un valor nuevo. self (con s minúscula) es el valor sobre el que se llamó el método.
La misma idea arregla una función libre, es decir, una que está fuera de cualquier bloque impl. Ahí rustc lo dice de forma un poco distinta: “this function can’t have a self parameter” (esta función no puede tener un parámetro self). En su lugar, pasa el struct por referencia:
struct Cart { items: Vec<u32>,}
fn total(cart: &Cart) -> u32 { cart.items.iter().sum()}
// llámala así: total(&cart)O mueve total dentro de impl Cart y dale &self, como en la Solución 1. Las dos opciones están bien; un método se lee más natural cuando la función claramente trata “sobre” un carrito.
Dónde lo verás normalmente
Sección titulada «Dónde lo verás normalmente»Dentro de new. Escribir self.count = 0; en fn new() -> Self da E0424, y la sugerencia de rustc, fn new(&self) -> Self, no tendría sentido (necesitarías un contador para crear un contador). Solución: construye el valor con Self { count: 0 }, como en la Solución 3.
Dentro de una closure en una función asociada. El problema no es la closure, sino la función que la rodea:
error[E0424]: expected value, found module `self` --> src/main.rs:8:35 |7 | fn sale_prices(prices: &[u32]) -> Vec<u32> { | ----------- this function doesn't have a `self` parameter8 | prices.iter().map(|p| p - self.discount).collect() | ^^^^ `self` value is a keyword only available in methods with a `self` parameterLa etiqueta vuelve a señalar la función sale_prices, que no tiene parámetro self. Solución: conviértela en método, fn sale_prices(&self, prices: &[u32]), o pasa el número como parámetro, fn sale_prices(prices: &[u32], discount: u32). Mira Closures.
En main o en una función auxiliar. self nunca funciona fuera de un bloque impl. Solución: pasa el struct como parámetro (total(&cart)), o llama a un método sobre él (cart.total()).
Si vienes de los métodos “static” de C# o Java. Una función asociada sin self es la versión de Rust de un método estático. Si quieres la versión de instancia, tienes que añadir tú el parámetro self. Mira Rust para desarrolladores de C#.
Errores relacionados
Sección titulada «Errores relacionados»- E0425: el despiste contrario. Estás en un método, pero escribiste
counten lugar deself.count. - E0599: “no method named … found” (no se encontró el método). Suele aparecer justo después de E0424, porque una función sin
selfno se puede llamar con un punto. - E0061: número incorrecto de argumentos, por ejemplo al llamar a
Player::label()sin el valor. - E0596: añadiste
&self, pero el método cambia un campo, así que necesita&mut self. Asignar a un campo de esta forma da E0594. - 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. Dentro de impl Player, fn heal() { self.hp += 10; } da E0424. ¿Cuál es la mejor solución?
2. ¿Por qué fn new() -> Self no puede usar self.name?
3. fn label(&self) -> String está definido en Player. ¿Qué llamada equivale a hero.label()?
Puntuación: 0 / 3
Para leer más
Sección titulada «Para leer más»- E0424 — Rust error codes index (en inglés): la explicación oficial de este error, con un ejemplo corto.
- Method Syntax — El libro de Rust (en inglés):
&self,&mut selfyself, además de funciones asociadas comonew. También existe una traducción al español de la comunidad del libro. - Associated functions and methods — Rust by Example (en inglés): ejemplos ejecutables de los dos tipos, uno al lado del otro.
- La palabra clave
self— documentación de la biblioteca estándar (en inglés): los dos significados deself, el receptor del método y el módulo actual. - Associated items — The Rust Reference (en inglés): las reglas exactas de los métodos, los receptores y las funciones asociadas.