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.
The error message
Section titled “The error message”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); // ❌ 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 methodThis 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.
What it means
Section titled “What it means”Look at the two labels rustc puts on your code:
- Line 6:
fn describe()has empty parentheses. rustc notes that “this function doesn’t have aselfparameter”. - Line 7:
self.nameasks for the name of “the player this was called on”. Butdescribewasn’t given a player, soselfmeans 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.
Why Rust does this
Section titled “Why Rust does this”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.
How to fix it
Section titled “How to fix it”Fix 1: Add &self, &mut self or self
Section titled “Fix 1: Add &self, &mut self or self”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: Ferrisrustc 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, bagelPacked: ["coffee", "bagel"]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.
Fix 2: Call it as Type::method(&value)
Section titled “Fix 2: Call it as Type::method(&value)”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)"]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”.
Fix 3: Pass in what the function needs
Section titled “Fix 3: Pass in what the function needs”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: 10Note 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.
Where you’ll usually see it
Section titled “Where you’ll usually see it”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` parameter8 | prices.iter().map(|p| p - self.discount).collect() | ^^^^ `self` value is a keyword only available in methods with a `self` parameterFix: 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.
Related errors
Section titled “Related errors”- E0425: the opposite slip. You’re in a method, but wrote
countinstead ofself.count. - E0599: “no method named … found”. Often printed right after E0424, because a function without
selfcan’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
Section titled “Quick check”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
Read more
Section titled “Read more”- E0424 — Rust error codes index: the official explanation of this error, with a short example.
- Method Syntax — The Rust Book:
&self,&mut selfandself, plus associated functions likenew. - Associated functions and methods — Rust by Example: runnable examples of both kinds side by side.
- The
selfkeyword — standard library docs: both meanings ofself, the method receiver and the current module. - Associated items — The Rust Reference: the exact rules for methods, receivers and associated functions.