Pular para o conteúdo

Erro E0424 do Rust: expected value, found module `self` (sem parâmetro self)

O E0424 quer dizer que você escreveu self dentro de uma função que não tem um parâmetro self. Então não existe nenhum “self” para o Rust apontar. É um deslize muito comum, principalmente para quem vem de Java, C# ou JavaScript, onde o this está sempre lá. A correção costuma ser uma palavra só: acrescentar &self na lista de parâmetros da função. Esta página faz parte do guia de erros do 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); // ❌ qual jogador?
| ^^^^ `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

Em português: “esperava um valor, encontrei o módulo self”. O compilador avisa que describe não tem parâmetro self, explica que self só existe em métodos com esse parâmetro e sugere acrescentar &self.

Este é o programa que gera o erro:

struct Player {
name: String,
}
impl Player {
fn describe() {
println!("Player: {}", self.name); // ❌ qual jogador?
}
}
fn main() {
let hero = Player { name: String::from("Ferris") };
hero.describe();
}

O segundo erro, o E0599 (“nenhum método chamado describe”), é só um efeito colateral do primeiro. Corrija o E0424 e ele some junto.

Veja os dois rótulos que o rustc coloca no seu código:

  1. Linha 6: fn describe() tem os parênteses vazios. O rustc avisa: “this function doesn’t have a self parameter” (esta função não tem um parâmetro self).
  2. Linha 7: self.name pede o nome “do jogador em que este método foi chamado”. Só que describe não recebeu jogador nenhum, então self não significa nada aqui.

Uma função dentro de um bloco impl sem parâmetro self é uma função associada (associated function). Ela pertence ao tipo (Player::describe()), não a um valor específico. Uma função com parâmetro self é um método, e só métodos podem usar self.

Pense num formulário que diz “escreva o seu nome aqui”. Ele só faz sentido quando alguém está segurando o formulário. describe() é um formulário que ninguém está segurando.

E por que a mensagem fala em “found module self”? Em Rust, self tem um segundo significado: o módulo atual, como em use self::helpers;. Quando não existe um valor self, o rustc recorre a esse outro significado e reclama que um módulo não é um valor. Pode ignorar a palavra “module”; o problema de verdade é o parâmetro que está faltando.

Em Rust, nada fica escondido. Java e C# dão a todo método não estático um this invisível. O Rust faz você escrever self como primeiro parâmetro, e você ainda diz como o método recebe o valor: só olhando, alterando ou tomando posse dele. Essa escolha faz parte das regras de borrowing (empréstimo), então precisa ficar visível.

Assim, todo mundo que lê o seu código, e o compilador também, sabe exatamente o que um método faz com o valor. fn summary(&self) promete não mudar nada. fn add(&mut self, ...) avisa que muda. fn checkout(self) consome o valor. E o compilador confere essas promessas para você. Métodos e blocos impl estão explicados em Structs.

Escolha esta quando a função deve trabalhar com um valor que já existe. Na maioria das vezes, é a correção certa. Acrescente o parâmetro que o rustc sugere e chame a função com ponto:

struct Player {
name: String,
}
impl Player {
fn describe(&self) {
println!("Jogador: {}", self.name);
}
}
fn main() {
let hero = Player { name: String::from("Ferris") };
hero.describe();
}

Saída:

Jogador: Ferris
▶ Experimente você mesmo

O rustc sempre sugere &self, mas existem três tipos. Escolha o que combina com o que o método faz:

Parâmetro O método… Nomes típicos
&self só lê o valor summary, len, is_empty, describe
&mut self altera o valor add, push, reset, level_up
self consome o valor (toma o ownership, a posse) into_..., finish, build, checkout

Aqui estão os três no mesmo tipo:

struct Order {
items: Vec<String>,
}
impl Order {
fn summary(&self) -> String {
format!("{} item(ns): {}", 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("pão de queijo");
println!("{}", order.summary());
let packed = order.checkout(); // order é consumido aqui
println!("Embalado: {:?}", packed);
}

Saída:

2 item(ns): café, pão de queijo
Embalado: ["café", "pão de queijo"]
▶ Experimente você mesmo

Se você escolher &self mas o método alterar um campo, o compilador avisa na hora (E0594, “cannot assign to self.count, which is behind a & reference”, ou E0596 para chamadas como push). Troque para &mut self. Na dúvida, comece com &self; o self puro é o caso mais raro.

Depois que a função tem um parâmetro self, dá para chamá-la de dois jeitos. hero.label() e Player::label(&hero) significam exatamente a mesma coisa: o valor que ficaria antes do ponto vira o primeiro argumento. A forma com ponto é a que você vai escrever quase sempre. A forma Tipo:: é útil quando você quer passar o próprio método para algo como map:

struct Player {
name: String,
level: u32,
}
impl Player {
fn label(&self) -> String {
format!("{} (nível {})", self.name, self.level)
}
}
fn main() {
let hero = Player { name: String::from("Ferris"), level: 3 };
println!("{}", hero.label());
println!("{}", Player::label(&hero)); // a mesma chamada
let team = vec![hero, Player { name: String::from("Corro"), level: 5 }];
let labels: Vec<String> = team.iter().map(Player::label).collect();
println!("{:?}", labels);
}

Saída:

Ferris (nível 3)
Ferris (nível 3)
["Ferris (nível 3)", "Corro (nível 5)"]
▶ Experimente você mesmo

O que não funciona é Player::label() sem nenhum valor: um método sempre precisa do seu self. Deixar ele de fora dá o E0061, “this function takes 1 argument but 0 arguments were supplied” (esta função recebe 1 argumento, mas nenhum foi passado).

Escolha esta quando ainda não existe um valor, ou quando a função nem está dentro de um bloco impl.

O caso clássico é o construtor. new cria o valor, então não há self de onde ler nada. Passe os dados iniciais para new como parâmetros normais e monte a struct com 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!("Contagem: {}", counter.count);
}

Saída:

Contagem: 10
▶ Experimente você mesmo

Repare nas maiúsculas: Self (com S maiúsculo) é o tipo, então Self { ... } cria um valor novo. self (com s minúsculo) é o valor em que o método foi chamado.

A mesma ideia corrige uma função solta, fora de qualquer bloco impl. Ali o rustc usa uma frase um pouco diferente: “this function can’t have a self parameter” (esta função não pode ter um parâmetro self). Passe a struct por referência:

struct Cart {
items: Vec<u32>,
}
fn total(cart: &Cart) -> u32 {
cart.items.iter().sum()
}
// chame assim: total(&cart)

Ou mova total para dentro de impl Cart e dê a ela um &self, como na Correção 1. Os dois jeitos estão certos; um método fica mais natural quando a função claramente “fala sobre” um carrinho.

Dentro de new. Escrever self.count = 0; em fn new() -> Self dá E0424, e a sugestão do rustc, fn new(&self) -> Self, não faria sentido (você precisaria de um contador para criar um contador). Correção: monte o valor com Self { count: 0 }, como na Correção 3.

Dentro de uma closure numa função associada. A closure não é o problema; a função em volta dela é:

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

A mensagem é a mesma: sale_prices não tem parâmetro self, então a closure também não pode usar self.discount. Correção: transforme em método, fn sale_prices(&self, prices: &[u32]), ou passe o número como parâmetro, fn sale_prices(prices: &[u32], discount: u32). Veja Closures.

Em main ou numa função auxiliar. self nunca funciona fora de um bloco impl. Correção: passe a struct como parâmetro (total(&cart)) ou chame um método nela (cart.total()).

Vindo dos métodos “static” do C# ou do Java. Uma função associada sem self é a versão do Rust para um método estático. Se você quer a versão de instância, precisa acrescentar o parâmetro self você mesmo. Veja Rust para desenvolvedores C#.

  • E0425: o deslize contrário. Você está num método, mas escreveu count em vez de self.count.
  • E0599: “no method named … found” (nenhum método com esse nome). Costuma aparecer logo depois do E0424, porque uma função sem self não pode ser chamada com ponto.
  • E0061: número errado de argumentos, por exemplo chamar Player::label() sem o valor.
  • E0596: você acrescentou &self, mas o método altera um campo, então precisa de &mut self. Atribuir a um campo desse jeito dá o E0594.
  • Todos os erros do compilador: a lista completa de páginas de erro.

Teste o que aprendeu

1. Dentro de impl Player, fn heal() { self.hp += 10; } dá E0424. Qual é a melhor correção?

2. Por que fn new() -> Self não pode usar self.name?

3. fn label(&self) -> String está definida em Player. Qual chamada é igual a hero.label()?

Pontuação: 0 / 3