Перейти к содержимому

Ошибка Rust E0424: expected value, found module `self` (self вне метода)

E0424 значит, что ты написал self внутри функции, у которой нет параметра self, так что Rust не на что указать словом «self». Это очень частая оплошность, особенно если ты пришёл из Java, C# или JavaScript, где this есть всегда. Обычно исправление — одно слово: добавь &self в список параметров функции. Эта страница — часть руководства по ошибкам компилятора.

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

Компилятор говорит: ожидалось значение, а найден модуль self; значение self доступно только в методах с параметром self. Подсказка предлагает добавить &self. Вот программа, которая выдаёт эту ошибку:

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();
}

Комментарий спрашивает: «какой игрок?». Вторая ошибка, E0599, — побочный эффект первой. Исправь E0424, и она тоже исчезнет.

Посмотри на две подписи, которые rustc ставит к твоему коду:

  1. Строка 6: у fn describe() пустые скобки. rustc отмечает: «this function doesn’t have a self parameter» (у этой функции нет параметра self).
  2. Строка 7: self.name просит имя «того игрока, у которого вызвали метод». Но describe никакого игрока не получала, поэтому self здесь ничего не значит.

Функция внутри блока impl без параметра self называется ассоциированной функцией (associated function). Она принадлежит типу (Player::describe()), а не какому-то одному значению. Функция с параметром self — это метод, и только методы могут говорить self.

Представь бланк с надписью «впиши сюда своё имя». Он имеет смысл, только когда кто-то держит его в руках. describe() — бланк, который никто не держит.

Почему там написано «found module self»? В Rust у self есть второе значение: текущий модуль, как в use self::helpers;. Когда значения self нет, rustc берёт это значение и жалуется, что модуль — не значение. На слово «module» можно не обращать внимания: настоящая проблема — отсутствующий параметр.

В Rust ничего не спрятано. Java и C# дают каждому нестатическому методу невидимый this. Rust заставляет написать self первым параметром, и ты заодно говоришь, как метод его получает: только смотрит, изменяет или забирает владение. Этот выбор — часть правил заимствования, поэтому он должен быть виден.

Так каждый, кто читает твой код, и компилятор точно знают, что метод делает со своим значением. fn summary(&self) обещает ничего не менять. fn add(&mut self, ...) предупреждает, что меняет. fn checkout(self) расходует значение. А компилятор проверяет эти обещания за тебя. Методы и блоки impl разобраны в уроке Структуры.

Выбирай это, когда функция должна работать с одним уже существующим значением. Чаще всего это правильное исправление. Добавь параметр, который предлагает rustc, и вызывай через точку:

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

Вывод:

Игрок: Ferris
▶ Попробуй сам

rustc всегда предлагает &self, но вариантов три. Выбери тот, что подходит к делу метода:

Параметр Метод… Типичные имена
&self только читает значение summary, len, is_empty, describe
&mut self изменяет значение add, push, reset, level_up
self расходует значение (забирает владение) into_..., finish, build, checkout

Вот все три на одном типе:

struct Order {
items: Vec<String>,
}
impl Order {
fn summary(&self) -> String {
format!("Позиций: {} — {}", 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("кофе");
order.add("бублик");
println!("{}", order.summary());
let packed = order.checkout(); // здесь order расходуется
println!("Упаковано: {:?}", packed);
}

Вывод:

Позиций: 2 — кофе, бублик
Упаковано: ["кофе", "бублик"]
▶ Попробуй сам

Если ты выбрал &self, а метод меняет поле, компилятор сразу скажет об этом (E0594 «cannot assign to self.count, which is behind a & reference» или E0596 для вызовов вроде push). Тогда переходи на &mut self. Если сомневаешься, начинай с &self; self нужен редко.

Когда у функции есть параметр self, её можно вызвать двумя способами. hero.label() и Player::label(&hero) значат одно и то же: значение, которое стоит перед точкой, становится первым аргументом. Чаще всего ты будешь писать через точку. Форма через Type:: удобна, когда хочешь передать сам метод куда-нибудь, например в map:

struct Player {
name: String,
level: u32,
}
impl Player {
fn label(&self) -> String {
format!("{} (уровень {})", self.name, self.level)
}
}
fn main() {
let hero = Player { name: String::from("Ferris"), level: 3 };
println!("{}", hero.label());
println!("{}", Player::label(&hero)); // тот же вызов
let team = vec![hero, Player { name: String::from("Corro"), level: 5 }];
let labels: Vec<String> = team.iter().map(Player::label).collect();
println!("{:?}", labels);
}

Вывод:

Ferris (уровень 3)
Ferris (уровень 3)
["Ferris (уровень 3)", "Corro (уровень 5)"]
▶ Попробуй сам

А вот Player::label() без значения не сработает: методу всегда нужен его self. Если его пропустить, получишь E0061, «this function takes 1 argument but 0 arguments were supplied».

Исправление 3: передай функции то, что ей нужно

Заголовок раздела «Исправление 3: передай функции то, что ей нужно»

Выбирай это, когда значения ещё нет или когда функция вообще не в блоке impl.

Классический случай — конструктор. new создаёт значение, так что self, из которого можно читать, нет. Передай new начальные данные обычными параметрами и собери структуру через 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!("Счёт: {}", counter.count);
}

Вывод:

Счёт: 10
▶ Попробуй сам

Обрати внимание на регистр: Self (с большой S) — это тип, поэтому Self { ... } создаёт новое значение. self (с маленькой s) — это значение, у которого вызвали метод.

Та же идея исправляет и свободную функцию, то есть функцию вне любого блока impl. Там rustc формулирует чуть иначе: «this function can’t have a self parameter» (у этой функции не может быть параметра self). Вместо этого передай структуру по ссылке:

struct Cart {
items: Vec<u32>,
}
fn total(cart: &Cart) -> u32 {
cart.items.iter().sum()
}
// вызов: total(&cart)

Или перенеси total в impl Cart и дай ей &self, как в исправлении 1. Оба варианта хороши; метод читается естественнее, когда функция явно «про» корзину.

Внутри new. Если написать self.count = 0; в fn new() -> Self, получишь E0424, а предложение rustc fn new(&self) -> Self смысла не имеет (чтобы сделать счётчик, понадобился бы счётчик). Исправление: собери значение через Self { count: 0 }, как в исправлении 3.

Внутри замыкания в ассоциированной функции. Проблема не в замыкании, а в функции вокруг него:

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

Это та же ошибка: у функции sale_prices нет параметра self. Исправление: сделай её методом, fn sale_prices(&self, prices: &[u32]), или передай число параметром, fn sale_prices(prices: &[u32], discount: u32). Подробнее в уроке Замыкания.

В main или во вспомогательной функции. self никогда не работает вне блока impl. Исправление: передай структуру параметром (total(&cart)) или вызови у неё метод (cart.total()).

После «статических» методов C# или Java. Ассоциированная функция без self — это версия статического метода в Rust. Если нужна версия для экземпляра, параметр self придётся добавить самому. Подробнее на странице Rust для разработчиков на C#.

  • E0425: обратная оплошность. Ты внутри метода, но написал count вместо self.count.
  • E0599: «no method named … found». Часто печатается сразу после E0424, потому что функцию без self нельзя вызвать через точку.
  • E0061: неверное число аргументов, например вызов Player::label() без значения.
  • E0596: ты добавил &self, но метод меняет поле, поэтому нужен &mut self. Присваивание полю в таком случае даёт E0594.
  • Все ошибки компилятора: полный список страниц с ошибками.

Проверь себя

1. Внутри impl Player функция fn heal() { self.hp += 10; } даёт E0424. Как лучше всего исправить?

2. Почему fn new() -> Self не может использовать self.name?

3. У Player есть fn label(&self) -> String. Какой вызов означает то же, что hero.label()?

Результат: 0 / 3