Метод 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

Хм, Инди советует прибегнуть к маневру 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 и убедиться, что он возвращает правильные результаты.