rust-lang / rust-lang/rust

`rustc` thinks that two non-intersecting borrows intersect. And fails compilation

Open
#139,930 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-borrow-checker C-bug fixed-by-polonius
Dominant language
Rust
Stars
119k
Forks
16.1k
PR merge metrics
PR metrics pending

Description

Here is a minimal example

fn main() {
    let mut data = "hello".to_owned();
    let out = process_data(&mut data);
    println!("{out:?}"); // Expected output: `Some("bye")`
}

/// Process data and return reference to its part.
fn process_data(data: &mut String) -> Option<&str> {
    loop {
        // Modify data in any way
        data.replace_range(0..5, "bye");

        // Access data via immutable reference, and try to reference with inherited lifetime.
        match find_word(data) {
            None => continue,
            Some(word) => return Some(word),
        }
    }
}

// You can use any function that takes a reference, and returns a related reference
fn find_word(data: &str) -> Option<&str> {
    let pattern = "bye";
    data.find(pattern)
        .map(|start| &data[start..(start + pattern.len())])
}

I expected to see this happen:

$ cargo run
Some("bye")

Instead, this happened: I have got a compile error on a perfectly valid code.

[ussur issue]$ rustc src/main.rs
error[E0502]: cannot borrow `*data` as mutable because it is also borrowed as immutable
  --> src/main.rs:11:9
   |
8  | fn process_data(data: &mut String) -> Option<&str> {
   |                       - let's call the lifetime of this reference `'1`
...
11 |         data.replace_range(0..5, "bye");
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
...
14 |         match find_word(data) {
   |                         ---- immutable borrow occurs here
15 |             None => continue,
16 |             Some(word) => return Some(word),
   |                                  ---------- returning this value requires that `*data` is borrowed for `'1`

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0502`.

The code being valid can be proved by the fact that if we remove the loop - the function compiles perfectly:

// YET this does compile correctly: exactly the same code without a loop.
fn process_data(data: &mut String) -> Option<&str> {
    data.replace_range(0..5, "bye");

    match find_word(data) {
        None => None,
        Some(word) => return Some(word),
    }
}

Moreover, breaking the loop by returning in no way breaks any borrowing rules. If reference is returned - then line that mutates data cannot be executed, and this error message makes no sense. If reference is not returned - this error message also make no sense.

UPD: Moreover, manually unwinding the loop also breaks compilation:

fn process_data_unnwinded(data: &mut String) -> Option<&str> {
    data.replace_range(0..5, "bye");

    match find_word(data) {
        None => {},
        Some(word) => return Some(word),
    };

    data.replace_range(0..5, "bye");

    match find_word(data) {
        None => {},
        Some(word) => return Some(word),
    };

    todo!()
}

Gives:

[ussur@ussur-maibookxseries issue]$ rustc src/main.rs 
error[E0502]: cannot borrow `*data` as mutable because it is also borrowed as immutable
  --> src/main.rs:37:5
   |
29 | fn process_data_unnwinded(data: &mut String) -> Option<&str> {
   |                                 - let's call the lifetime of this reference `'1`
...
32 |     match find_word(data) {
   |                     ---- immutable borrow occurs here
33 |         None => {},
34 |         Some(word) => return Some(word),
   |                              ---------- returning this value requires that `*data` is borrowed for `'1`
...
37 |     data.replace_range(0..5, "bye");
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0502`.

Even though immutable borrow ends after match {} ends - rustc for some invalid reason thinks that returning a reference makes it borrowed "forever".

Meta

Bug works the same both on stable and nightly.
rustc --version --verbose of stable:

rustc 1.85.1 (4eb161250 2025-03-15)
binary: rustc
commit-hash: 4eb161250e340c8f48f66e2b929ef4a5bed7c181
commit-date: 2025-03-15
host: x86_64-unknown-linux-gnu
release: 1.85.1
LLVM version: 19.1.7

of nightly:

rustc 1.87.0-nightly (a2e63569f 2025-03-26)
binary: rustc
commit-hash: a2e63569fd6702ac5dd027a80a9fdaadce73adae
commit-date: 2025-03-26
host: x86_64-unknown-linux-gnu
release: 1.87.0-nightly
LLVM version: 20.1.1

Backtrace does not exist since program does not compile.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by compiling the minimal process_data example with the reported stable and nightly rustc versions, then compare it with the non-loop and manually unrolled variants. Investigate the E0502 diagnostic and borrow-checking behavior around find_word, match, and return; done means the valid examples compile without introducing borrow-checking regressions.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
compilers
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.