Why does changing `FnOnce` to `FnMut` cause `borrowed data escapes outside of closure` error?
⚓ Rust 📅 2025-11-26 👤 surdeus 👁️ 9I try to grasp the trick that controls the borrowing lifetime for a specific variable, like what the standard std::thread::scope does. I simplified the utility to the following:
use std::marker::PhantomData;
struct Scope<'env>{
_phantom:PhantomData<* mut &'env ()>,
}
impl<'env> Scope<'env>{
fn spawn<F>(self:& Scope<'env>,_f:F) where F:FnMut()+'env{
}
}
fn start<'env,F>(mut f:F)
where F:FnOnce(& Scope<'env>)+'env{ // #1
let scope:Scope<'env> = Scope{_phantom: PhantomData::default()};
f(&scope);
}
fn main() {
let mut i = 0;
start(|s|{
s.spawn(||{
let rf = & mut i; // #2
});
// let rf = & mut i; // #3
});
}
It does work. If you uncomment #3, then you will get the expected error:
error[E0499]: cannot borrow `i` as mutable more than once at a time
--> src\main.rs:22:16
|
18 | start(|s|{
| - has type `&Scope<'1>`
19 | s.spawn(||{
| - -- first mutable borrow occurs here
| _______|
| |
20 | | let rf = & mut i;
| | - first borrow occurs due to use of `i` in closure
21 | | });
| |________- argument requires that `i` is borrowed for `'1`
22 | let rf = & mut i;
| ^^^^^^^ second mutable borrow occurs here
However, if I change FnOnce at #1 to FnMut, then #1 directly causes an obscure error without commenting #3. The error is:
error[E0521]: borrowed data escapes outside of closure
--> src\main.rs:19:7
|
18 | start(|s|{
| - `s` declared here, outside of the closure body
19 | / s.spawn(||{
20 | | let rf = & mut i;
21 | | });
| |________^
|
= note: requirement occurs because of the type `Scope<'_>`, which makes the generic argument `'_` invariant
= note: the struct `Scope<'env>` is invariant over the parameter `'env`
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
I don't know the reason here. Where does the constraint that requires s to be alive outside the first closure body? IIUC, the receiver type of spawn is & Scope<'env>, where the implicit lifetime is unconstrained and is covariant. Moreover, the type wouldn't require the implicit lifetime outlives 'env.
Why does changing FnOnce to FnMut cause the error? How to understand it?
2 posts - 2 participants
🏷️ Rust_feed