Skip to content

borrow checker too restrictive in match statements, with confusing/confused compile errors #17462

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
makoConstruct opened this issue Sep 23, 2014 · 6 comments
Labels
A-borrow-checker Area: The borrow checker C-bug Category: This is a bug. fixed-by-NLL Bugs fixed, but only when NLL is enabled. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@makoConstruct
Copy link

struct Node{  left:Option<Box<Node>>  }

fn leftmost_child(n:& mut Node)-> & mut Node {
    let mut leftest = n;
    loop {
        match leftest.left.as_mut() {
            Some(nl) => leftest = &mut **nl,
            None => break,
        }
    }
    leftest
}

fn main(){}

yeilds

<anon>:6:9: 6:21 note: previous borrow of `leftest.left` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `leftest.left` until the borrow ends
<anon>:6        match leftest.left.as_mut() {
                      ^~~~~~~~~~~~
<anon>:12:2: 12:2 note: previous borrow ends here
<anon>:3 fn leftmost_child(n:& mut Node)-> & mut Node {
...
<anon>:12 }
          ^
<anon>:7:16: 7:35 error: cannot assign to `leftest` because it is borrowed
<anon>:7            Some(nl) => leftest = &mut **nl,
                                ^~~~~~~~~~~~~~~~~~~
<anon>:6:9: 6:21 note: borrow of `leftest` occurs here
<anon>:6        match leftest.left.as_mut() {
                      ^~~~~~~~~~~~
<anon>:11:2: 11:9 error: cannot borrow `*leftest` as mutable more than once at a time
<anon>:11   leftest
            ^~~~~~~
<anon>:6:9: 6:21 note: previous borrow of `leftest.left` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `leftest.left` until the borrow ends
<anon>:6        match leftest.left.as_mut() {
                      ^~~~~~~~~~~~
<anon>:12:2: 12:2 note: previous borrow ends here
<anon>:3 fn leftmost_child(n:& mut Node)-> & mut Node {
...
<anon>:12 }
          ^

http://is.gd/7aqog5
Note that there's another error about being unable to assign to leftest within the match body. Should borrowing forbid reassignment here?

@makoConstruct makoConstruct changed the title confusing/confused compiler complaint that a match expression can't borrow a member because it is already borrowed, by that match statement. confusing/confused compiler complaint that a match expression can't borrow a member because it is already borrowed, by that match expression. Sep 23, 2014
@steveklabnik steveklabnik added A-type-system Area: Type system A-diagnostics Area: Messages for errors, warnings, and lints labels Jan 27, 2015
@UserAB1236872
Copy link

This seems to be the same issue I'm having:

use std::cmp::Eq;

enum List<T: Eq> {
    Cons {
        val: T,
        next: Box<List<T>>,
    },
    Nil,
}

impl <T: Eq> List<T> {
    fn find_mut(&mut self, query: T) -> &mut List<T> {
        use List::{Cons, Nil};
        match *self {
            Nil => self,
            Cons{ref val, ref mut next} =>
            if *val == query { self } else { next.find_mut(query) },
        }
    }
}

The example is a bit useless, but that's about as minimal as I could get it. The actual case was a Binary Search Tree. Playground link, but the short version is you get the error cannot borrow '*self' as mutable because 'self.val' is also borrowed as immutable.

This is despite the fact that all the ref borrows will end upon return from the match. Without returning self from the Cons clause, it compiles fine. I can't even really find a good workaround for this without forcing T to implement the Copy trait.

@UserAB1236872
Copy link

Perhaps the title should be changed to something like "Borrow checker is too sensitive to borrows in pattern matching."

Another example has shown up on stack overflow.

@makoConstruct makoConstruct changed the title confusing/confused compiler complaint that a match expression can't borrow a member because it is already borrowed, by that match expression. borrow checker too restrictive in match statements, with confusing/confused compile errors Feb 28, 2015
@birkenfeld
Copy link
Contributor

Doesn't look there anything diagnostic-related to do here.

@steveklabnik steveklabnik added the T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. label Mar 9, 2017
@Mark-Simulacrum Mark-Simulacrum added A-borrow-checker Area: The borrow checker and removed A-diagnostics Area: Messages for errors, warnings, and lints A-type-system Area: Type system labels May 30, 2017
@Mark-Simulacrum Mark-Simulacrum added the C-bug Category: This is a bug. label Jul 22, 2017
@steveklabnik
Copy link
Member

Triage: this code still does not compile

@shepmaster
Copy link
Member

shepmaster commented Sep 24, 2018

Note that this will be fine once NLL stabilizes:

struct Node {
    left: Option<Box<Node>>,
}

fn leftmost_child(n: &mut Node) -> &mut Node {
    let mut leftest = n;
    loop {
        match leftest.left {
            Some(ref mut nl) => leftest = &mut **nl,
            None => break,
        }
    }
    leftest
}

fn main() {}

Or more idiomatically:

struct Node {
    left: Option<Box<Node>>,
}

fn leftmost_child(n: &mut Node) -> &mut Node {
    let mut leftest = n;
    while let Some(ref mut nl) = leftest.left {
        leftest = nl;
    }
    leftest
}

fn main() {}

See also the canonical answer for this on Stack Overflow, which shows how to make it work before NLL.

@shepmaster shepmaster added the fixed-by-NLL Bugs fixed, but only when NLL is enabled. label Sep 24, 2018
@pnkfelix
Copy link
Member

NLL (migrate mode) is enabled in all editions as of PR #59114. Verified that test case compiled in Nightly 2015 edition; closing as fixed.

lnicola pushed a commit to lnicola/rust that referenced this issue Jun 23, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-borrow-checker Area: The borrow checker C-bug Category: This is a bug. fixed-by-NLL Bugs fixed, but only when NLL is enabled. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

7 participants