Skip to content

Rust Error E0424: Expected Value, Found Module `self`

E0424 means you wrote self inside a function that doesn’t have a self parameter, so there is no “self” for Rust to point at. It’s a very common slip, especially if you come from Java, C# or JavaScript, where this is always there. The fix is usually one word: add &self to the function’s parameter list. This page is part of the compiler errors guide.

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); // ❌ which player?
| ^^^^ `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

This is the program that produces it:

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

The second error, E0599, is a side effect of the first. Fix E0424 and it goes away too.

Look at the two labels rustc puts on your code:

  1. Line 6: fn describe() has empty parentheses. rustc notes that “this function doesn’t have a self parameter”.
  2. Line 7: self.name asks for the name of “the player this was called on”. But describe wasn’t given a player, so self means nothing here.

A function inside an impl block without a self parameter is an associated function. It belongs to the type (Player::describe()), not to one particular value. A function with a self parameter is a method, and only methods can say self.

Think of a form that says “write your own name here”. It only makes sense once someone is holding the form. describe() is a form nobody is holding.

Why does it say “found module self”? In Rust, self has a second meaning: the current module, as in use self::helpers;. When there is no self value, rustc falls back to that meaning and then complains that a module isn’t a value. You can ignore the word “module”; the real problem is the missing parameter.

In Rust, nothing is hidden. Java and C# give every non-static method an invisible this. Rust makes you write self as the first parameter, and you also say how the method gets it: just looking, changing, or taking ownership. That choice is part of the borrowing rules, so it has to be visible.

This tells everyone reading your code, and the compiler, exactly what a method does to its value. fn summary(&self) promises not to change anything. fn add(&mut self, ...) warns that it changes things. fn checkout(self) uses the value up. The compiler then checks those promises for you. Methods and impl blocks are explained in Structs.

Choose this when the function is supposed to work on one existing value. That’s the right fix most of the time. Add the parameter rustc suggests and call it with a dot:

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

Output:

Player: Ferris
▶ Try it Yourself

rustc always suggests &self, but there are three kinds. Pick the one that matches what the method does:

Parameter The method… Typical names
&self only reads the value summary, len, is_empty, describe
&mut self changes the value add, push, reset, level_up
self uses the value up (takes ownership) into_..., finish, build, checkout

Here are all three on one type:

struct Order {
items: Vec<String>,
}
impl Order {
fn summary(&self) -> String {
format!("{} item(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("coffee");
order.add("bagel");
println!("{}", order.summary());
let packed = order.checkout(); // order is used up here
println!("Packed: {:?}", packed);
}

Output:

2 item(s): coffee, bagel
Packed: ["coffee", "bagel"]
▶ Try it Yourself

If you pick &self but the method changes a field, the compiler tells you right away (E0594 “cannot assign to self.count, which is behind a & reference”, or E0596 for calls like push). Switch to &mut self. When in doubt, start with &self; self is the rare one.

Once a function has a self parameter, you can call it two ways. hero.label() and Player::label(&hero) mean exactly the same thing: the value you’d put before the dot becomes the first argument. The dot form is what you’ll write most of the time. The Type:: form is handy when you want to pass the method itself to something like map:

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

Output:

Ferris (level 3)
Ferris (level 3)
["Ferris (level 3)", "Corro (level 5)"]
▶ Try it Yourself

What does not work is Player::label() with no value: a method always needs its self. Leaving it out gives E0061, “this function takes 1 argument but 0 arguments were supplied”.

Choose this when there is no value yet, or when the function isn’t in an impl block at all.

The classic case is a constructor. new creates the value, so there’s no self to read from. Give new the starting data as normal parameters and build the struct with 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!("Count: {}", counter.count);
}

Output:

Count: 10
▶ Try it Yourself

Note the capital letters: Self (capital S) is the type, so Self { ... } builds a new value. self (small s) is the value a method was called on.

The same idea fixes a free function, one outside any impl block. rustc words it slightly differently there: “this function can’t have a self parameter”. Pass the struct in by reference instead:

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

Or move total into impl Cart and give it &self, as in Fix 1. Both are fine; a method reads more naturally when the function is clearly “about” a cart.

Inside new. Writing self.count = 0; in fn new() -> Self gives E0424, and rustc’s suggestion fn new(&self) -> Self would make no sense (you’d need a counter to make a counter). Fix: build the value with Self { count: 0 }, as in Fix 3.

Inside a closure in an associated function. The closure is not the problem; the function around it is:

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

Fix: make it a method, fn sale_prices(&self, prices: &[u32]), or pass the number in, fn sale_prices(prices: &[u32], discount: u32). See Closures.

In main or a helper function. self never works outside an impl block. Fix: pass the struct in as a parameter (total(&cart)), or call a method on it (cart.total()).

Coming from C# or Java “static” methods. An associated function without self is Rust’s version of a static method. If you want the instance version, you must add the self parameter yourself. See Rust for C# developers.

  • E0425: the opposite slip. You’re in a method, but wrote count instead of self.count.
  • E0599: “no method named … found”. Often printed right after E0424, because a function without self can’t be called with a dot.
  • E0061: wrong number of arguments, for example calling Player::label() without the value.
  • E0596: you added &self, but the method changes a field, so it needs &mut self. Assigning to a field this way gives E0594.
  • All compiler errors: the full list of error pages.

Quick check

1. Inside impl Player, fn heal() { self.hp += 10; } gives E0424. What is the best fix?

2. Why can't fn new() -> Self use self.name?

3. fn label(&self) -> String is defined on Player. Which call is the same as hero.label()?

Score: 0 / 3