На начальном у всех новичков возникают проблемы с тем что значит self в rust и чем он отличается от Self.
Ниже приведен пример кода из книги «Rust Профессиональное программирование». В книге допущена ошибка, я отметил ее в комментарии.
Ожидался self, но случайно был напечатан Self и программа перестала компилироваться. Посмотрите этот код, он отлично иллюстрирует сразу несколько особенностей языка, но раз уж речь пошла о self, обратите внимание как он используется в примере.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
/* * В этом списке отсутствуют операции вставки. * Можно попробовать реализовать их самостоятельно. * Список никогда не будет пустым, недействительным или содержать нулевые указатели. */ // В каждом элементе списка имеются упакованные в нем данные. // Поле не может быть пустым или нулевым. struct ListItem { data: Box, next: Option<Box<ListItem>>, // Значение None = конец списка } // Структура списка содержит только заголовок // упаковка которого нас не волнует поскольку он обязателен struct SinglyLinkedList { head: ListItem } impl ListItem { fn new(data: T) -> Self { ListItem { // Новые данные перемещаются в кучу при помощи Box data: Box::new(data), next: None } } // Next - возвращает для каждого элемента опциональную ссылку // если она существует. Нужен для упрощения кода распаковки элементов. fn next(&self) -> Option<&Self> { // проверка наличия связи для элемента if let Some(next) = &self.next { // Возвращение следующей ссылки на элемент // Some(&*next) - эквивалентно Some(next.as_ref()) } else { None } } fn mut_tail(&mut self) -> &mut Self { // if let здесь не сработает, поскольку мы не можем одновременно // заимствовать self.next и возвращать изменяемую ссылку на внутренний указатель. if self.next.is_some() { // Box внутри Options требует распаковки из изменяемой ссылки // и вернуть изменяемую ссылку изнутри self.next.as_mut().unwrap().mut_tail() } else { // Если элемента нет, это хвост и возвращаем self self // В книге здесь Self } } // Получаем fn data(&self) -> &T { self.data.as_ref() } } impl SinglyLinkedList { // Для нового списка нужен первый элемент. Чтобы допустить наличие // пустого списка задан как Option() fn new(data: T) -> Self { SinglyLinkedList { head: ListItem::new(data), } } fn append(&mut self, data: T) { let tail = self.head.mut_tail(); // Добавляем новый элемент в хвост tail.next = Some(Box::new(ListItem::new(data))) } fn head(&self) -> &ListItem { &self.head } } fn main() { let mut list = SinglyLinkedList::new("head"); list.append("middle"); list.append("tail"); let mut item = list.head(); loop { println!("item: {}", item.data()); if let Some(next_item) = item.next() { item = next_item; } else { break; } } } /* * "Для новичка Rust связянный список будет отличной возможностью узнать об уникальных особенностях языка". * Бренден Мэтьюз. Rust профессиональное программирование */ |
В Rust Self и self — это совершенно разные сущности, несмотря на схожесть написания. Разберём их по пунктам.
self (строчная буква)
Что это: первый параметр метода, представляющий экземпляр типа, для которого вызывается метод.
Особенности:
- Всегда пишется со строчной буквы.
- Является значением (конкретным объектом).
- Может иметь разные формы владения:
self— передаёт владение (объект «уходит» в метод);&self— неизменяемая ссылка (только чтение);&mut self— изменяемая ссылка (можно менять поля).
Self (заглавная буква)
Что это: ключевое слово, обозначающее текущий тип в контексте реализации.
Особенности:
- Пишется с заглавной буквы.
- Является типом, а не значением.
- Автоматически подставляется компилятором как синоним текущего типа.
- Особенно полезен в трейтах и при обобщённом программировании.
Следуя рекомендациям по синтаксису Rust мы знаем, что типажи и структуры пишутся с заглавной буквы, в то время как переменные принято писать со строчной. Когда втягиваешься, это оказывается удобно. Пример с S(s)elf явное тому доказательство.
В некоторых языках применяются this и self. И такой подход так же создает немало путаницы не только у новичков.
Так как же никогда не запутаться? Ответ прост: никак. В своем коде вы будете допускать подобные ошибки. К сожалению это неизбежно, когда для разных обстоятельств применяется одно слово и разница между совершенно разными сущностями — регистр заглавной буквы, ошибки неизбежны.
Чтобы отлавливать такие ошибки в больших массивах кода, научитесь читать сообщения об ошибках компилятора. Внимательно и вдумчиво изучая, что заставило компилятор вывести эти сообщения.




