Skip to content

better documentation of reborrowing #788

Open

Description

tl;dr: Rust documentation is unclear on reborrowing, should explain details in the Reference

As it stands, there is only one mention of "reborrowing" in the Reference, in this section on raw pointers. This only refers to reborrowing pointers, and not references, which is important in understanding how Rust handles mutable references (often implicitly reborrowing them instead of moving them). There is also some documentation on implicit borrowing, but again, this is sparse and does not cover reborrowing in the context of mutable references.

A key example of the implicit borrowing which confused me is the following:

fn main() {
   	let mut x = vec![1, 2, 3];
   	let y = &mut x;
   	let z: &mut Vec<i32> = y;
   	z.push(4);
   	y.push(5);
}

This code compiles fine, and x, as expected, becomes [1, 2, 3, 4, 5]. Initially, this code was unclear to me, but I was able to understand how it worked learning about both reborrowing and non lexical lifetimes. However, along the way I became familiar with how little documentation there is on the former. Questions have come up on the topic frequently, like this Reddit post from 2016, this post from 2018 on the Rust forums, more Reddit posts from 2018 and 2019, and in this forum post from January, which I found extremely helpful. A few of the discussions in these posts reference sources to reborrowing, but the only official documentation which was available is an earlier version of the Rustonomicon. There are some instances of the author of The Rust Programming Language, such as here, here, and in rust-lang/rust#25899, mentioning adding reborrowing to the new book, but it seems that has been abandoned since: see rust-lang/book#2144.

This concept is counter-intuitive to beginners, as it seems to violate the idea that there can only be one mutable reference to an object in a given scope. It also raises frequent forum questions, which become an effective way to get answers, but only due to the lack of documentation. Because of this, I think that some proper documentation on reborrowing is necessary. It would explain some of the quirks of this system, as choosing whether or not to reborrow is a decision made by the compiler, and can lead to some strange compilation errors without understanding this concept. For example,

let mut x = vec![1, 2, 3];
let y = &mut x;
let z = y;

will move y into z, but substituting the last line for

let z: &mut _ = y;

will reborrow y, since the compiler has to guarantee that z is &mut T. y can be freely used after the second case, but not at all after the first case. Rust has also made some progress with reborrowing, as issues like reborrowing in match arms which failed to compile in the past now work fine, without the need to force a move. The compiler can infer when it is necessary to move a &mut instead of reborrow it, but the logic behind this decision should be made clear to the programmer so that there is a better understanding of the final product.

It seems like there was some effort to document this topic (through non-lexical lifetimes) in #290. This documentation is unfinished, potentially due to the in-progress nature of non-lexical lifetimes, but the feature is stable, which means it could accompany a discussion of reborrowing. I think reborrowing is an important enough topic to get covered in some official documentation, and if not the book, then the reference should expand upon this technique.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions