Spurious / unnecessary double borrow error with `match Box::pin`
Open
Nobody has claimed this yet.
A-diagnostics
A-pin
A-spurious
C-bug
T-compiler
- Dominant language
- Rust
- Stars
- 119k
- Forks
- 16.1k
- PR merge metrics
- PR metrics pending
Description
use std::task::Context;
use std::pin::Pin;
use std::task::Poll;
struct Foo<'buf> {
data: &'buf String,
}
async fn read_foo<'buf>(buffer: &'buf mut String) -> Result<Foo<'buf>, ()> {
Ok(Foo {
data: buffer
})
}
fn poll_next(mut buffer: Pin<&mut String>, cx: &mut Context<'_>) -> Poll<()> {
// error on double mut borrow - requires match value to be put in let variable
match Box::pin(read_foo(&mut *buffer)).as_mut().poll(cx) {
Poll::Ready(Ok(foo)) => {
println!("foo: {}", foo.data);
return Poll::Ready(());
},
Poll::Ready(Err(())) => {
println!("buffer: {}", buffer);
return Poll::Ready(());
}
Poll::Pending => return Poll::Pending,
};
}
I expected the code to compile - the Poll::Ready(Err) match arm should have access to buffer. Putting the match value into a let binding makes the code compile (example here).
Instead, the below compiler error is generated:
error[E0502]: cannot borrow `buffer` as immutable because it is also borrowed as mutable
--> src/lib.rs:23:36
|
16 | match Box::pin(read_foo(&mut *buffer)).as_mut().poll(cx) {
| --------------------------------
| | |
| | mutable borrow occurs here
| a temporary with access to the mutable borrow is created here ...
...
23 | println!("buffer: {}", buffer);
| ^^^^^^ immutable borrow occurs here
...
28 | };
| - ... and the mutable borrow might be used here, when that temporary is dropped and runs the destructor for type `Pin<Box<impl Future<Output = Result<Foo<'_>, ()>>>>`
Meta
rustc --version --verbose:
rustc 1.91.1 (ed61e7d7e 2025-11-07)
binary: rustc
commit-hash: ed61e7d7e242494fb7057f2657300d9e77bb4fcb
commit-date: 2025-11-07
host: x86_64-pc-windows-msvc
release: 1.91.1
LLVM version: 21.1.2
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Reproduce the example from the Rust Playground link, or place it in src/lib.rs, and compare the failing inline match with the let-binding variant. Start by tracing the temporary borrow and drop behavior in the compiler. Done means the inline match compiles as expected and the behavior has regression coverage.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 42/100