Skip to content
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

Conflict with mutable/immutable borrows #22

Open
mikepurvis opened this issue Dec 9, 2020 · 3 comments
Open

Conflict with mutable/immutable borrows #22

mikepurvis opened this issue Dec 9, 2020 · 3 comments

Comments

@mikepurvis
Copy link

mikepurvis commented Dec 9, 2020

Hey, thanks for the nifty package! I'm a newer rustacean just getting my feet wet doing advent of code, and today's puzzle had a situation where I wanted to defer some processing until the scope exited:

fn find_number(preamble_len: usize, numbers: &[u64]) -> Option<u64> {
    let mut iter = numbers.iter();
    let mut preamble: VecDeque<&u64> = iter.by_ref().take(preamble_len).collect();
    for value in iter {
        defer! {
            preamble.pop_front();
            preamble.push_back(value);
        }
        if preamble.iter().copied().combinations(2)
            .any(|c| c.iter().copied().sum::<u64>() == *value) {
            continue;
        }
        return Some(*value);
    }
    None
}

I totally appreciate that the VecDeque is overkill here and just using a sliding slice would have more than done the job, but I'm curious about why I have trouble compiling this:

   Compiling day-9 v0.1.0 (advent-of-code\2020\day-9)
error[E0502]: cannot borrow `preamble` as immutable because it is also borrowed as mutable
  --> src\main.rs:24:12
   |
20 | /         defer! {
21 | |             preamble.pop_front();
   | |             -------- first borrow occurs due to use of `preamble` in closure
22 | |             preamble.push_back(value);
23 | |         }
   | |_________- mutable borrow occurs here
24 |           if preamble.iter().copied().combinations(2)
   |              ^^^^^^^^ immutable borrow occurs here
...
29 |       }
   |       - mutable borrow might be used here, when `_guard` is dropped and runs the `Drop` code for type `ScopeGuard`

error: aborting due to previous error

When I just paste the code in at the two possible exit points, it accepts it fine, so I'm having trouble understanding what the issue is here with defer's internal closure. Is there some way to resolve this or is it intractable?

@bluss
Copy link
Owner

bluss commented Dec 10, 2020

defer is using a closure, you can use a macro less version of that to see it better - but it has the borrow checker errors as any closure would have. The version of the scope guard that takes and owns a value exists to solve this problem (see examples in docs).

@mikepurvis
Copy link
Author

Hey, thanks for the response. Based on the example in the docs, I made it to here:

fn find_number(preamble_len: usize, numbers: &[u64]) -> Option<u64> {
    let mut iter = numbers.iter();
    let mut preamble: VecDeque<&u64> = iter.by_ref().take(preamble_len).collect();
    for value in iter {
        let mut preamble2 = scopeguard::guard(preamble, |mut preamble| {
            preamble.pop_front();
            preamble.push_back(value);
        });
        if preamble2.iter().copied().combinations(2)
            .any(|c| c.iter().copied().sum::<u64>() == *value) {
            continue;
        }
        return Some(*value);
    }
    None
}

However, I think this situation is more complex than what you show in the doc, because the value being mutated is outside the scope that I want to exit on, so I'm getting a bunch of value moved on previous iteration type issues.

It seems pretty clear that the complexity required here isn't worth it, but as a purely academic exercise, I'd be delighted to figure out how to make this work.

Here's a playground link if you're interested to experiment: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=c4407108eabe72ab21604d7c09d43159

@bluss
Copy link
Owner

bluss commented Dec 11, 2020

The academic solution is scopeguard::guard(&mut preamble

Innovations welcome, in this crate or elsewhere 🙂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants