Skip to content

Detect attempt to reuse buffer with extended borrows #147694

@estebank

Description

@estebank

Given code like the following three cases where the user intended to reuse a previously allocated mutable buffer in a loop where the buffer is populated with borrows which affect the lifetime of the buffer itself, the errors are always E0597 "doesn't live long enough" pointing at the intermediary data being used.

fn process_sources(sources: Vec<Source>) {
    let mut buffer: Vec<&[u8]> = vec![];
    for source in sources {
        let data: Vec<u8> = source.fetch_data();
        buffer.extend(data.split(splitter));
        process_data(&buffer);
        buffer.clear();
    }
}
error[E0597]: `data` does not live long enough
 --> src/lib.rs:5:23
  |
4 |         let data: Vec<u8> = source.fetch_data();
  |             ---- binding `data` declared here
5 |         buffer.extend(data.split(splitter));
  |         ------        ^^^^ borrowed value does not live long enough
  |         |
  |         borrow later used here
...
8 |     }
  |     - `data` dropped here while still borrowed
fn process_sources(sources: Vec<Source>) {
    let mut buffer: Vec<&[u8]> = vec![];
    for source in sources {
        buffer.clear();
        let data: Vec<u8> = source.fetch_data();
        buffer.extend(data.split(splitter));
        process_data(&buffer);
    }
}
error[E0597]: `data` does not live long enough
 --> src/lib.rs:6:23
  |
4 |         buffer.clear();
  |         ------ borrow later used here
5 |         let data: Vec<u8> = source.fetch_data();
  |             ---- binding `data` declared here
6 |         buffer.extend(data.split(splitter));
  |                       ^^^^ borrowed value does not live long enough
7 |         process_data(&buffer);
8 |     }
  |     - `data` dropped here while still borrowed
fn process_sources(sources: Vec<Source>) {
    let mut buffer: Vec<&[u8]> = vec![];
    for source in sources {
        let data: Vec<u8> = source.fetch_data();
        buffer.extend(data.split(splitter));
        process_data(&buffer);
    }
}
error[E0597]: `data` does not live long enough
 --> src/lib.rs:5:23
  |
4 |         let data: Vec<u8> = source.fetch_data();
  |             ---- binding `data` declared here
5 |         buffer.extend(data.split(splitter));
  |         ------        ^^^^ borrowed value does not live long enough
  |         |
  |         borrow later used here
6 |         process_data(&buffer);
7 |     }
  |     - `data` dropped here while still borrowed

The compiler should detect cases of buffer re-use and explain what's going on, pointing at the buffer declaration.

Ideally, it should recommend using recycle_vec (or the potential std API of the same use), or the unsafe code required to reconstitute the buffer within the loop without reallocation.

Case 1 in https://databento.com/blog/why-we-didnt-rewrite-our-feed-handler-in-rust, additional discussion in https://internals.rust-lang.org/t/same-old-issues-recurring-self-referential-types-generics-less-flexible-than-c-templates/23603

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-borrow-checkerArea: The borrow checkerA-diagnosticsArea: Messages for errors, warnings, and lintsA-lifetimesArea: Lifetimes / regionsD-newcomer-roadblockDiagnostics: Confusing error or lint; hard to understand for new users.D-terseDiagnostics: An error or lint that doesn't give enough information about the problem at hand.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions