Why compiler prefer failed than choosing move?

I have a question about closre capture rules.

Document says:

Capture modes

The compiler prefers to capture a closed-over variable by immutable borrow, followed by unique immutable borrow (see below), by mutable borrow, and finally by move. It will pick the first choice of these that allows the closure to compile.

While, the following fn a() does not compiles, seems compiler prefer using mutable borrow instread of move. That is conflict with document:
it will pick the first choice of these that allows the closure to compile.

While, if I use another let uu = uc; in fn b() then compiler will use move.

I don't know why fn a() can't compiles just like fn b().

#[test]
fn a() {
    let mut uc = UnsafeCell::new(9);
    let j = thread::spawn(|| {
        *uc.get_mut() = 100;
    });
    j.join();
}
#[test]
fn b() {
    use std::cell::UnsafeCell;
    let mut uc = UnsafeCell::new(9);
    let j = thread::spawn(|| {
        let mut uu = uc;
        *uu.get_mut() = 100;
    });
    j.join();
}

The reference also says

The choice is made only with regards to the contents of the closure expression; the compiler does not take into account surrounding code, such as the lifetimes of involved variables.

so this is not actually in conflict. The compiler looks at how the captured variables are used in the closure's body and decides on a capture mode based on that. In this case uc.get_mut() is just a mutable use of uc, so that's what you get.

The reason this is done is that lifetime requirements are checked later in the compilation process, and type inference (which includes closure type inference) runs before that.

Perhaps it should be phrased, "allows to closure to type check".

Opened https://github.com/rust-lang/reference/pull/1064

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.