I am testing my understanding of lifetimes in Rust by explicitly annotating function signatures and I created an example that I am not sure I understand.
In this example, I am simulating the concept of sharing a book and turning a page within it. To do this I am using a single mutable reference which I pass to a borrow_and_read
function that updates the curr_page
field of a Book
struct. My Book
struct and main
function look like:
#[derive(Debug)]
pub struct Book<'a> {
pub title: &'a str,
pub curr_page: Option<i32>,
pub page_count: i32,
}
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),
};
}
fn main() {
let mut the_book: Book = Book {
title: "The Book",
curr_page: None,
page_count: 104,
};
let a_book: &mut Book = &mut the_book;
borrow_and_read(a_book);
borrow_and_read(a_book);
observe_book(&*a_book);
}
pub fn observe_book<'a>(a_book: &'a Book<'a>) {
println!("Observing: {:?}", a_book);
}
For my first implementation of the borrow_and_read
function, I let the compiler add annotations and everything compiled:
fn borrow_and_read(a_book: &mut Book) {
match a_book.curr_page {
Some(page) => a_book.curr_page = Some(page + 1),
None => a_book.curr_page = Some(0),
};
}
I then tried adding a single lifetime annotation specifying a lifetime for both the reference and the instance of the Book
itself:
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),
};
}
This yielded the following errors:
error[E0499]: cannot borrow `*a_book` as mutable more than once at a time
--> src/main.rs:25:21
|
24 | borrow_and_read(a_book);
| ------ first mutable borrow occurs here
25 | borrow_and_read(a_book);
| ^^^^^^
| |
| second mutable borrow occurs here
| first borrow later used here
error[E0502]: cannot borrow `*a_book` as immutable because it is also borrowed as mutable
--> src/main.rs:27:18
|
24 | borrow_and_read(a_book);
| ------ mutable borrow occurs here
...
27 | observe_book(&*a_book);
| ^^^^^^^^
| |
| immutable borrow occurs here
| mutable borrow later used here
After thinking through what I had initially tried, I decided it made sense to separate the lifetimes of the mutable reference to a Book
and the instance of Book
itself. I then came up with this:
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),
};
}
which does compile and output the expected results.
I am confused as to why my initial error message was that a_book
was borrowed mutably more than once. I thought I would be ok passing around a single mutable reference since each usage of the reference understood that the reference was mutable. This thinking seems to be confirmed by the final implementation of my borrow_and_read
function but I am not completely sure why specifying that the lifetime of the Book
instance outlives the mutable reference with where 'b : 'a
fixes my issue.
I am hoping to get a solid understanding of how using the same lifetime for both the mutable reference and Book
instance yield the errors I got.