Проблема с вашим оригиналом в том, что время жизни слишком ограничено. Поскольку заимствование у Book
имеет ту же длину, что и заимствование по названию книги ("The Book"
), изменяемый заем вынужден длиться столько же, сколько и сама книга, а это означает, что он никогда не может быть заимствован неизменно.
Давайте рассмотрим это. Будет проще изучить вашу фиксированную версию, а затем посмотреть, что делает оригинал, чтобы ограничить ее.
fn borrow_and_read<'a, 'b>(a_book: &'a mut Book<'b>)
where 'b : 'a {
match a_book.curr_page {
Some(page) => a_book.curr_page = Some(page + 1),
None => a_book.curr_page = Some(0),
};
}
Эта функция имеет два параметра времени жизни: один для самой книги и один для изменяемого заимствования на книга. Мы также ограничиваем 'b: 'a
, что означает, что любые займы со сроком действия 'a
действительны не дольше, чем займы со сроком действия 'b
. Это на самом деле избыточно , так как компилятор может это видеть в любом случае. Имея аргумент с типом &'a mut Book<'b>
, 'a
уже не может длиться дольше, чем 'b
.
Теперь давайте посмотрим на main
. Мы назовем время жизни самой книги 'book
. Мы будем называть время жизни изменчивого заимствования книги 'mtb
. Наконец, мы назовем неизменный заем (на observe_book
) 'imb
. Давайте посмотрим, как долго должна длиться каждая жизнь.
// Initialize `the_book`. 'book has to start before this.
// Mutably borrow `the_book`. 'mtb has to start here.
let a_book: &mut Book = &mut the_book;
// Use the mutable borrow. 'mtb has to still be valid.
borrow_and_read(a_book);
// Use the mutable borrow. 'mtb has to still be valid.
borrow_and_read(a_book);
// Deref the mutable borrow and reborrow immutably.
// 'imb has to start here, so 'mtb has to end here.
// 'imb is a reference to `the_book`, so 'book has to still be active.
observe_book(&*a_book);
// The variables are no longer needed, so any outstanding lifetimes can end here
// That means 'imb and 'book end here.
Итак, суть проблемы заключается в том, что при этой настройке 'mtb
должен закончиться до 'book
. Теперь давайте посмотрим на исходную версию функции.
fn borrow_and_read<'a>(a_book: &'a mut Book<'a>) {
match a_book.curr_page {
Some(page) => a_book.curr_page = Some(page + 1),
None => a_book.curr_page = Some(0),
};
}
Теперь у нас есть только один параметр времени жизни, который заставляет время жизни заголовка и время изменяемого заимствования быть одинаковыми. Это означает, что 'mtb
и 'book
должны быть одинаковыми. Но мы только что показали, что 'mtb
должен закончиться до 'book
! Таким образом, с этим противоречием компилятор выдает нам ошибку. Я не знаю технических деталей того, почему ошибка cannot borrow
* a_book as mutable more than once at a time
, но я представляю, что компилятор думает об «использовании» переменной так же, как мы говорим о временах жизни. Поскольку 'book
должно длиться до тех пор, пока вызов observe_book
и 'mtb
не будет таким же, как 'book
, он рассматривает использование 'book
как использование изменяемого заимствования. Опять же, я не совсем уверен в этом. Возможно, стоит подать вопрос, чтобы посмотреть, можно ли улучшить сообщение.
Я действительно сделал ie чуть выше. Хотя Rust не выполняет неявное приведение типов, он выполняет приведение к жизни. Займы с более длительным сроком службы могут быть вынуждены заимствовать с более коротким сроком службы. В конечном счете, это не имеет большого значения, но об этом стоит знать.
Название книги, строковый литерал, имеет тип &'static str
, где 'static
- это особое время жизни, которое длится для вся продолжительность программы. Данные встраиваются в двоичный код самой программы. Когда мы инициализируем the_book
, он может иметь тип Book<'static>
, но он также может быть приведен к Book<'book>
для некоторого более короткого времени жизни 'book
. Когда мы берем изменчивый заем, мы вынуждены иметь 'book: 'mtb
, но у нас все еще нет других ограничений.
Когда мы вызываем однопараметрическую версию borrow_and_read
, 'book
и 'mtb
оба должны быть сокращены до более короткого общего срока службы. (в этом случае, поскольку 'book: 'mtb
, 'mtb
будет работать - и действительно, это будет самый длинный срок службы, который будет работать). В двухпараметрической версии принуждение не требуется. 'book
и 'mtb
можно использовать как есть.
Теперь, когда мы разыменовываем a_book
и постоянно его заимствуем, изменяемые заимствования не могут быть активными. Это означает, что mtb
и более короткое время жизни, что и 1067 *, и 'mtb
были вынуждены закончиться. Но у a_book
есть время жизни 'book
, и мы его используем, поэтому 'book
не может закончиться. Отсюда ошибка.
В двухпараметрической версии 'book
не был приведен к сокращению срока службы, поэтому он может продолжаться.