В Rust: в чём разница между self и Self (заглавная и строчная буквы)?
Ключевое отличие: тип vs значение
В Rust Self (с заглавной S) и self (со строчной s) — это два совершенно разных понятия, хотя они и связаны с текущим типом.
Self— это псевдоним типа. Он всегда ссылается на тип, для которого реализуется текущий блокimpl. Это аналогthisв других языках, но только для типа, а не для экземпляра.self— это значение-экземпляр. Это первый аргумент метода, который представляет собой сам объект, на котором вызывается метод. По сути, этоself: Self— сокращённая запись для параметра.
Пример для наглядности
Вернёмся к вашему коду. Давайте перепишем его с явными типами, чтобы стало понятнее:
struct Point {
x: f64,
y: f64,
}
impl Point {
// Здесь Self — это Point
fn new(x: f64, y: f64) -> Self { // Self = Point
Self { x, y } // Создаём экземпляр Point
}
// Здесь:
// self — это переменная типа Point (точнее &Point)
// other — это другой экземпляр Point
// Self — это тоже Point
fn distance(&self, other: &Self) -> f64 {
// self.x — обращаемся к полю x текущего экземпляра
// other.x — обращаемся к полю x другого экземпляра
let dx = self.x - other.x;
let dy = self.y - other.y;
(dx * dx + dy * dy).sqrt()
}
}
Теперь разберём каждый случай:
1. Self — псевдоним типа
Self можно использовать везде, где ожидается имя типа: в возвращаемом значении, в параметрах, в объявлениях переменных. Это особенно удобно, когда:
- Вы работаете с обобщёнными типами (generics) —
Selfавтоматически подставит конкретный тип. - Вы переименовываете структуру — не придётся менять все вхождения в
impl-блоке. - Вы пишете трейты — там
Selfобязателен, так как реализующий тип неизвестен заранее.
// Пример с трейтом
trait Area {
fn area(&self) -> f64;
}
impl Area for Point {
fn area(&self) -> f64 {
0.0 // Точка не имеет площади
}
}
Внутри impl Area for Point слово Self будет означать Point.
2. self — аргумент-экземпляр
self — это сокращённая форма записи первого параметра метода. Полная форма выглядит так:
fn distance(self: &Self, other: &Self) -> f64 { ... }
Rust позволяет писать просто &self вместо self: &Self. Есть три варианта:
self— забирает владение (редко, только если нужно уничтожить объект).&self— неизменяемая ссылка (самый частый случай).&mut self— изменяемая ссылка.
3. Почему в new нет self?
Функция new — это ассоциированная функция (associated function), а не метод. Она не принимает self, потому что вызывается на типе, а не на экземпляре:
let p = Point::new(3.0, 4.0); // Вызов на типе, не на объекте
Поэтому внутри new нет доступа к self.x — просто потому, что self (экземпляра) ещё не существует. Self же есть — это тип Point.
Короткое правило для запоминания
Self— это имя типа (какPoint). Используйте его везде, где нужно указать тип.self— это имя переменной (какthisв других языках). Используйте его как первый аргумент метода.
И да, вместо Self можно написать Point — это будет работать. Но использование Self делает код более гибким и читаемым, особенно при работе с дженериками и трейтами.
Практический совет: как не путаться
Когда я только начинал писать на Rust, я использовал такой ментальный трюк: заменял Self на слово "Тип", а self на "Я". Это помогает сразу понять контекст.
impl Point {
// "Тип" new возвращает новый экземпляр "Типа"
fn new(x: f64, y: f64) -> Self { ... }
// "Я" (self) сравниваюсь с другим "Типом"
fn distance(&self, other: &Self) -> f64 { ... }
}
Ещё один пример для закрепления
Рассмотрим ситуацию, когда у нас есть перечисление (enum):
enum Color {
Red,
Green,
Blue,
}
impl Color {
// Self здесь — это Color
fn random() -> Self {
// Генерация случайного цвета
todo!()
}
// self — это конкретный экземпляр Color
fn is_primary(&self) -> bool {
matches!(self, Color::Red | Color::Green | Color::Blue)
}
}
Как видите, Self работает с любыми типами: структурами, перечислениями, даже с примитивами (хотя для них редко пишут impl).
Важно: Self всегда пишется с заглавной буквы, а self — со строчной. Это строгое соглашение в Rust, и компилятор выдаст ошибку, если вы перепутаете регистр.
Со временем вы привыкнете, и различие станет интуитивно понятным. Главное — запомнить: Self = тип, self = экземпляр.
Ответов пока нет.
Войдите чтобы оставить ответ
Блоги