Ir al contenido

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.

error[E0424]: expected value, found module `self`
--> src/main.rs:7:32
|
6 | fn describe() {
| -------- this function doesn't have a `self` parameter
7 | 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.

Mira las dos etiquetas que rustc pone en tu código:

  1. Línea 6: fn describe() tiene los paréntesis vacíos. rustc te avisa: “this function doesn’t have a self parameter” (esta función no tiene un parámetro self).
  2. Línea 7: self.name pide el nombre de “el jugador sobre el que se llamó la función”. Pero describe no recibió ningún jugador, así que aquí self no 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.

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.

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: Ferris
▶ Pruébalo tú mismo

rustc 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é, tostada
Empaquetado: ["café", "tostada"]
▶ Pruébalo tú mismo

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)"]
▶ Pruébalo tú mismo

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: 10
▶ Pruébalo tú mismo

Fí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.

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` parameter
8 | prices.iter().map(|p| p - self.discount).collect()
| ^^^^ `self` value is a keyword only available in methods with a `self` parameter

La 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#.

  • E0425: el despiste contrario. Estás en un método, pero escribiste count en lugar de self.count.
  • E0599: “no method named … found” (no se encontró el método). Suele aparecer justo después de E0424, porque una función sin self no 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 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