Метод push
Пришло время написать функцию вставки числа в начало списка.
Вставка (push) изменяет список, поэтому нам нужен параметр &mut self.
Кроме того, нам потребуется значение i32, которое мы будем вставлять.
impl List {
pub fn push(&mut self, elem: i32) {
// написать
}
}
Прежде всего нам надо создать узел и сохранить в нём наш элемент:
pub fn push(&mut self, elem: i32) {
let new_node = Node {
elem: elem,
next: ?????
};
}
Что должно быть по ссылке next?
Ну, весь наш старый список!
Можем ли мы... просто так и написать?
impl List {
pub fn push(&mut self, elem: i32) {
let new_node = Node {
elem: elem,
next: self.head,
};
}
}
> cargo build
error[E0507]: cannot move out of borrowed content
--> src/first.rs:19:19
|
19 | next: self.head,
| ^^^^^^^^^ cannot move out of borrowed content
Неееееет. Наверное, Rust написал всё правильно, но непонятно, что это значит и что с этим делать:
cannot move out of borrowed content
Нельзя переместить заимствованное содержимое.
Мы пытаемся переместить поле self.head в next, но Rust не разрешает нам это сделать.
Проблема в том, что когда мы завершим заимствование и «вернём назад» законному владельцу, self останется частично инициализированным.
Как мы уже говорили, это единственное, что нельзя сделать с помощью &mut.
Rust очень вежлив, а это — невежливо (кстати, и опасно тоже, но вряд ли компилятор беспокоит какая-то опасность).
Что если вместо старого значения мы разместим узел, который мы создаём:
pub fn push(&mut self, elem: i32) {
let new_node = Box::new(Node {
elem: elem,
next: self.head,
});
self.head = Link::More(new_node);
}
> cargo build
error[E0507]: cannot move out of borrowed content
--> src/first.rs:19:19
|
19 | next: self.head,
| ^^^^^^^^^ cannot move out of borrowed content
Ничего не вышло.
В принципе, это одна из тех штук, которую Rust мог бы принять, но не принимает (по многим причинам, одна из которых — это [безопасность исключений][]).
Нам нужен способ забрать значение head так, чтобы Rust не заметил пропажи.
Обратимся за советом к печально известному взломщику ржавых замков Индиане Джонсу:

Хм, Инди советует прибегнуть к маневру mem::replace.
Эта невероятно полезная функция позволяет нам забрать значение заимствованного объекта, заменив его другим значением.
Добавим std::mem в начало файла, чтобы модуль mem оказался в локальной области видимости:
use std::mem;
и используем надлежащим образом:
pub fn push(&mut self, elem: i32) {
let new_node = Box::new(Node {
elem: elem,
next: mem::replace(&mut self.head, Link::Empty),
});
self.head = Link::More(new_node);
}
Здесь мы временно заменяем (replace) self.head значением Link::Empty перед тем, как заменить его новой головой списка.
Не стану врать: так делать не надо (по меньшей мере, это решение не простое, и не очевидное), но в учебных целях мы должны проверить все грабли.
Позже мы познакомимся с простым и очевидным способом.
Но, подождите, метод push уже готов!
Кажется.
Честно говоря, это стоит протестировать.
Как это сделать проще всего?
Написать метод pop и убедиться, что он возвращает правильные результаты.