This semester I have an opportunity to do an independent study on the Rust compiler, supervised by my professor David Evans. The particular topic I’m thinking of exploring right now is the Rust lifetime system. This blog will serve as a place where I can document my progress (unless I feel lazy about blogging and this becomes the last post). It will also (hopefully) serve the dual purpose of showing professor Evans that I’m not slacking off.
If I have to rate my current knowledge of Rust, on a scale of 0 (no clue) to 10 (mastery), I would put myself at a 3. Embarrassingly, one crucial topic that I have always had trouble dealing with is Rust lifetime. People often say that we should keep learning different languages to explore new ways of thinking. For Rust, the new thing it offers is making the programmer explicitly aware of the lifetime of objects. Thus, given my lack of understanding of lifetime, I have plenty of reasons to study up on it.
As such, if anything I write is incorrect, I would appreciate it if someone were to point it out.
My current plan is to 1) read up the documentation on lifetime and the borrow-checker code (which handles lifetime) and 2) improve the lifetime error messages. After going 1/4th of the way through the documentation
middle::borrowck::gather_loans::doc, I feel that I understand it a little better now, and a few ideas have formed in my head.
This idea wasn’t thought of by me but given by Niko Matsakis and Patrick Walton. The idea is to detect common error patterns and suggest a fix. For example, currently, if we compile the following function:
1 2 3 4
we get all these scary-looking messages
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
It would be nice if in this particular case, we can just tell the user to introduce a lifetime parameter, or even better, give them something like: “perhaps you mean to declare
fn bar<'a>(x: &'a Foo) -> &'a int?”
One feedback I should seek from others is: are all those notes necessary? Personally, my eyes just glance over them. I have never understood them. Perhaps there are other instances where they may be useful.
When the Rust compiler reports an error because of a previous borrow, it may be helpful to note where that borrow ends. For example, currently the program
1 2 3 4 5
1 2 3 4 5 6
It may be good to add something like:
1 2 3 4 5 6
One case this would be helpful in is when the borrow ends earlier or later than the user expects, and so he has a chance to correct his misconception. On the other hand, it adds extra noise to the compiler, and the compiler is already pretty noisy.
I will begin this part with examples. First, let’s modify the program in Idea #2 a little bit. We will change the last line of the function so that
z borrows from
x mutably instead:
1 2 3 4 5
Compiling it would yield the following messages:
1 2 3 4 5 6
While these messages and the messages in Idea #2 are correct, they have two shortcomings:
A mutable borrow prevents both mutable and immutable borrows. The cause of these two errors are essentially the same, but currently it sounds like they are different. It is also a bit misleading to output “error: cannot borrow
xas immutable because it is also borrowed as mutable” when it cannot even be borrowed as mutable.
They fail to convey one key point: a borrow of a variable subsequently restricts the usage of that variable in some ways until that borrow ends.
Thus, in this particular instance, I think we would be well served by outputting the same error message for both and elaborating the note:
1 2 3 4 5 6 7
My only qualm is the wording does not convey explicitly that the restriction is placed on the variable only (perhaps “subsequent borrow or modification using variable
x” would be better?)
To summarize Idea #3, I believe that borrow checker errors can be conveyed better by focusing on the restriction that the original borrow places rather than reporting the precise details at the error site.