Stream: t-compiler/wg-nll

Topic: double mutable linked list traversal


Jake Goulding (Nov 14 2018 at 15:05, on Zulip):
#[derive(Debug, Clone)]
pub struct Node {
    elem: i32,
    next: Option<Box<Node>>,
}

fn example(mut list: &mut Node) {
    // Skip forward 2 nodes
    let mut i = 0;
    while let Some(node) = &mut list.next {
        if i == 2 {
            break;
        }
        i += 1;
        list = node;
    }

    // Skip forward 2 nodes
    let mut i = 0;
    while let Some(node) = &mut list.next {
        if i == 2 {
            break;
        }
        i += 1;
        list = node;
    }
}
error[E0499]: cannot borrow `leading.next` as mutable more than once at a time
  --> src/lib.rs:43:28
   |
28 |     while let Some(node) = &mut leading.next {
   |                            ----------------- first mutable borrow occurs here
...
43 |     while let Some(node) = &mut leading.next {
   |                            ^^^^^^^^^^^^^^^^^
   |                            |
   |                            second mutable borrow occurs here
   |                            first borrow used here, in later iteration of loop
Jake Goulding (Nov 14 2018 at 15:06, on Zulip):

What am I missing here? The first loop works fine, but the second loop causes the error

Jake Goulding (Nov 14 2018 at 15:38, on Zulip):

@Santiago Pastorino / @nikomatsakis Is this code relevant to your loop diagnostic discussion?

nikomatsakis (Nov 14 2018 at 15:39, on Zulip):

I don't think it's relevant

nikomatsakis (Nov 14 2018 at 15:39, on Zulip):

the error is curious

Jake Goulding (Nov 14 2018 at 15:39, on Zulip):

Ok, cause this also has " in later iteration of loop", so your discussion caught my eye

nikomatsakis (Nov 14 2018 at 15:39, on Zulip):

trying to decide if it's a bug :)

nikomatsakis (Nov 14 2018 at 15:39, on Zulip):

yes, indeed

nikomatsakis (Nov 14 2018 at 15:39, on Zulip):

it might be, not entirely sure

Jake Goulding (Nov 14 2018 at 15:40, on Zulip):

and it's two distinct loops (shrug)

nikomatsakis (Nov 14 2018 at 15:40, on Zulip):

I think it might just be another instance of https://github.com/rust-lang/rust/issues/47680

nikomatsakis (Nov 14 2018 at 15:41, on Zulip):

the reasoning feels similar

nikomatsakis (Nov 14 2018 at 15:41, on Zulip):

basically, in one case, the borrow ends because no data

nikomatsakis (Nov 14 2018 at 15:41, on Zulip):

in the other branch (when i==2 is false) the variable is overwritten

nikomatsakis (Nov 14 2018 at 15:41, on Zulip):

this is basically "the case" that polonius aims to fix

nikomatsakis (Nov 14 2018 at 15:41, on Zulip):

well, this plus "Problem Case #3" from the original NLL RFC

nikomatsakis (Nov 14 2018 at 15:41, on Zulip):

we should call this Problem Case #4 =)

Jake Goulding (Nov 14 2018 at 15:44, on Zulip):

Oh, problem #4

Jake Goulding (Nov 14 2018 at 15:45, on Zulip):

Yeah, to me #3 is all about cross-function interaction of borrows, but I don't see any function calls here.

Jake Goulding (Nov 14 2018 at 15:47, on Zulip):

Reduced

pub struct Node {
    next: Option<Box<Node>>,
}

fn example(mut list: &mut Node) {
    if let Some(node) = &mut list.next {
        list = node;
    }

    if let Some(node) = &mut list.next {
        list = node;
    }
}
error[E0499]: cannot borrow `list.next` as mutable more than once at a time
  --> src/lib.rs:10:25
   |
5  | fn example(mut list: &mut Node) {
   |                      - let's call the lifetime of this reference `'1`
6  |     if let Some(node) = &mut list.next {
   |                 ----    -------------- first mutable borrow occurs here
   |                 |
   |                 assignment requires that `list.next` is borrowed for `'1`
...
10 |     if let Some(node) = &mut list.next {
   |                         ^^^^^^^^^^^^^^ second mutable borrow occurs here
Jake Goulding (Nov 14 2018 at 15:49, on Zulip):

Do you think I should add these to that issue, or create a new one and cross-link them? If it is problem #4, then a separate issue seems appropriate

nikomatsakis (Nov 14 2018 at 17:09, on Zulip):

I'm not sure if you and I mean the same things by 3 and 4 =)

nikomatsakis (Nov 14 2018 at 17:09, on Zulip):

I agree this has nothing to do with Problem Case #3, which is all about returning something from the fn

nikomatsakis (Nov 14 2018 at 17:09, on Zulip):

but I think it is the same as #47680, which (unless I am forgetting something) also had nothing to do with returning things

nikomatsakis (Nov 14 2018 at 17:09, on Zulip):

I think i'd prefer a comment on #47680

nikomatsakis (Nov 14 2018 at 17:10, on Zulip):

but a new issue is also ok, as long as the two are interlinked

Jake Goulding (Nov 14 2018 at 19:11, on Zulip):

@nikomatsakis #47680 has all sorts of borrows optionally crossing function boundaries:

The linked list example here has no function calls at all.

To me, NLL #3 is "borrow checker understands when function maybe returns a borrow". I don't know what NLL#4 would be yet, but it certainly seems distinct from this.

Jake Goulding (Nov 14 2018 at 19:14, on Zulip):

oh wait #47680... is the opposite?

The mutable borrow is released when matching on a Option<&mut Self> in a function, but not when the function is inlined

Last update: Nov 21 2019 at 13:50UTC