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

Ошибка Rust E0411: cannot find type Self in this scope (Self вне impl)

E0411 значит: ты написал Self там, где Rust не может понять, что оно обозначает. Self — это сокращение для «типа, которому посвящён этот блок impl (или трейт)», поэтому оно работает только внутри impl, трейта или определения типа. Обычная причина — конструктор вроде fn new() -> Self, написанный на верхнем уровне файла, или метод, скопированный из своего блока impl. Ошибка частая и исправляется быстро. В отличие от E0412, которая в Rust 1.93 превратилась в E0425, этот код не менялся: rustc 1.97 по-прежнему выводит E0411. Страница входит в раздел Ошибки компилятора Rust с объяснениями.

error[E0411]: cannot find type `Self` in this scope
--> src/main.rs:5:21
|
5 | fn new_counter() -> Self { // ❌ `Self` outside an `impl`
| ----------- ^^^^ `Self` is only available in impls, traits, and type definitions
| |
| `Self` not allowed in a function

По-русски: «не найден тип Self в этой области видимости». Ниже rustc уточняет, что Self доступно только в impl, трейтах и определениях типов, а в обычной функции его использовать нельзя.

Вот программа, которая её вызывает (комментарий // ❌ оставлен на английском, потому что rustc цитирует эту строку; он значит «Self вне impl»):

struct Counter {
count: u32,
}
fn new_counter() -> Self { // ❌ `Self` outside an `impl`
Counter { count: 0 }
}
fn main() {
let c = new_counter();
println!("{}", c.count);
}

rustc указывает на две вещи в строке 5:

  1. ^^^^ под Self: «Self is only available in impls, traits, and type definitions» — Self доступно только в impl, трейтах и определениях типов. Это и есть правило, в одной строке.
  2. ----- под new_counter: «Self not allowed in a function» — в функции Self нельзя. Это причина в нашем случае: new_counter — обычная функция, а не часть какого-то impl.

Ты-то знаешь, что new_counter возвращает Counter, ведь только что написал Counter { count: 0 }. Но Rust не читает тело функции, чтобы угадать, что значит Self. Смысл Self появляется только из охватывающего impl Counter { ... } или trait ... { ... }, а здесь его нет.

Это как слово «здесь» на стикере. На карте «вы здесь» понятно. А на записке, которая лежит на кухонном столе, никто не знает, где это «здесь».

Self — не отдельный тип. Это заполнитель, который подставляет каждый impl или трейт: внутри impl Counter Self значит Counter, внутри impl Timer — Timer, а внутри трейта — «тот тип, который реализует этот трейт». Именно поэтому Self удобно: можно переименовать структуру или скопировать метод в другой impl, и Self последует за ним.

Свободная функция не принадлежит никакому типу, поэтому подставлять нечего. Rust мог бы попытаться угадать по телу, но тогда сигнатура зависела бы от кода внутри, а Rust намеренно проверяет сигнатуры сами по себе. Где обычно живёт fn new() -> Self, рассказано в уроке Структуры.

Это нужно чаще всего. Функция, которая создаёт значение одного типа или работает с ним, должна жить в блоке impl этого типа, и там Self работает так, как ты ожидал. Вызывай её как Counter::new():

struct Counter {
count: u32,
}
impl Counter {
fn new() -> Self {
Self { count: 0 }
}
fn increment(&mut self) {
self.count += 1;
}
}
fn main() {
let mut c = Counter::new();
c.increment();
c.increment();
println!("{}", c.count);
}

Вывод:

2
▶ Попробуй сам

Внутри impl Self можно использовать и как тип (-> Self), и для создания значения (Self { count: 0 }). Подробнее — в уроке Структуры.

Выбирай этот вариант, когда функция должна остаться свободной — например, это помощник, который создаёт значения разных типов, или он живёт в другом модуле. Просто напиши настоящий тип, Counter, там, где было Self:

struct Counter {
count: u32,
}
fn new_counter(start: u32) -> Counter {
Counter { count: start }
}
fn reset(c: &mut Counter) {
c.count = 0;
}
fn main() {
let mut c = new_counter(10);
println!("{}", c.count);
reset(&mut c);
println!("{}", c.count);
}

Вывод:

10
0
▶ Попробуй сам

Так же исправляется и fn reset(c: &mut Self), скопированная из impl: каждое Self в сигнатуре и в теле становится Counter.

Исправление 3: Используй обобщённый тип, если имел в виду «любой тип»

Заголовок раздела «Исправление 3: Используй обобщённый тип, если имел в виду «любой тип»»

Выбирай этот вариант, когда ты написал Self в смысле «какой тип захочет вызывающий код». Это работа для обобщённого T, а не для Self. Объяви <T> с трейтами, которые нужны функции. Здесь T: Default позволяет функции создать начальное значение любого типа:

fn fresh_pair<T: Default>() -> (T, T) {
(T::default(), T::default())
}
fn main() {
let numbers: (u32, u32) = fresh_pair();
let words: (String, String) = fresh_pair();
println!("{:?}", numbers);
println!("{:?}", words);
}

Вывод:

(0, 0)
("", "")
▶ Попробуй сам

Если хочешь, чтобы каждый тип сам решал, как его создавать, помести функцию в трейт: trait Reset { fn fresh() -> Self; }. Внутри трейта Self значит «тип, который его реализует». Подробнее — в уроках Обобщения и Трейты.

Self { ... } в main или в свободной функции. let c = Self { count: 0 }; даёт E0411 с немного другим заголовком: cannot find struct, variant or union type `Self` in this scope. Исправление: напиши Counter { count: 0 } или вызови Counter::new().

Вспомогательная функция внутри метода. Если внутри impl Counter метод содержит собственную fn helper() -> Self, получится другой код — E0401: can't use `Self` from outer item («нельзя использовать Self из внешнего элемента»). Функция внутри функции не наследует внешний impl. Исправление: напиши Counter во внутренней функции, используй замыкание или вынеси helper в impl как отдельный метод.

Путаница между Self и self. Self с большой буквы — это тип, а self с маленькой — значение, у которого вызван метод. self вне метода — другая ошибка, E0424. Исправление в обоих случаях: убедись, что код стоит внутри нужного блока impl.

  • E0424: self (с маленькой буквы, значение) используется вне метода.
  • E0425: не найдено значение, функция или (начиная с Rust 1.93) тип с обычным именем.
  • E0412: старый код для «cannot find type», теперь сообщается как E0425.
  • E0405: не найден трейт, обычно из-за пропущенной строки use.
  • E0401: Self из внешнего impl во вложенной функции, см. выше.
  • Все ошибки компилятора: полный список страниц об ошибках.

Проверь себя

1. Где можно использовать Self как тип?

2. fn make_user() -> Self на верхнем уровне даёт E0411. Как это обычно исправляют?

3. Что создаёт Self { count: 0 } внутри impl Counter?

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