Ошибка 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:
^^^^подSelf: «Selfis only available in impls, traits, and type definitions» —Selfдоступно только вimpl, трейтах и определениях типов. Это и есть правило, в одной строке.-----подnew_counter: «Selfnot allowed in a function» — в функцииSelfнельзя. Это причина в нашем случае:new_counter— обычная функция, а не часть какого-тоimpl.
Ты-то знаешь, что new_counter возвращает Counter, ведь только что написал Counter { count: 0 }. Но Rust не читает тело функции, чтобы угадать, что значит Self. Смысл Self появляется только из охватывающего impl Counter { ... } или trait ... { ... }, а здесь его нет.
Это как слово «здесь» на стикере. На карте «вы здесь» понятно. А на записке, которая лежит на кухонном столе, никто не знает, где это «здесь».
Почему Rust так делает
Заголовок раздела «Почему Rust так делает»Self — не отдельный тип. Это заполнитель, который подставляет каждый impl или трейт: внутри impl Counter Self значит Counter, внутри impl Timer — Timer, а внутри трейта — «тот тип, который реализует этот трейт». Именно поэтому Self удобно: можно переименовать структуру или скопировать метод в другой impl, и Self последует за ним.
Свободная функция не принадлежит никакому типу, поэтому подставлять нечего. Rust мог бы попытаться угадать по телу, но тогда сигнатура зависела бы от кода внутри, а Rust намеренно проверяет сигнатуры сами по себе. Где обычно живёт fn new() -> Self, рассказано в уроке Структуры.
Как исправить
Заголовок раздела «Как исправить»Исправление 1: Перенеси функцию в блок impl
Заголовок раздела «Исправление 1: Перенеси функцию в блок impl»Это нужно чаще всего. Функция, которая создаёт значение одного типа или работает с ним, должна жить в блоке 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 }). Подробнее — в уроке Структуры.
Исправление 2: Напиши имя типа вместо Self
Заголовок раздела «Исправление 2: Напиши имя типа вместо Self»Выбирай этот вариант, когда функция должна остаться свободной — например, это помощник, который создаёт значения разных типов, или он живёт в другом модуле. Просто напиши настоящий тип, 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);}Вывод:
100Так же исправляется и 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
Что почитать
Заголовок раздела «Что почитать»- E0411 — Rust error codes index (на английском): официальное объяснение, что обозначает
Selfи почему оно работает только в impl, трейте или определении типа. - Ассоциированные функции — книга «Язык программирования Rust» (перевод на русский): ассоциированные функции вроде
fn square(size: u32) -> Selfи какSelfработает вimpl. - The
Selfkeyword — документация стандартной библиотеки (на английском): что значитSelfв impl, трейтах и определениях типов. Selfin paths — The Rust Reference (на английском): точные правила, в том числе почему вложенные элементы не могут использовать внешнийSelf.