北海道苫小牧市出身の初老PGが書くブログ

永遠のプログラマを夢見る、苫小牧市出身のおじさんのちらしの裏

NLLとDropトレイト

NLL (non-lexical lifetimes) について誤解していた。

次のコードは NLL のおかげで、 _y に代入した &x が次の行以降使われていないため、 &mut で可変参照を生成できる。

use anyhow::Result;

struct X<'a> (&'a i32);

fn main() -> Result<()> {
    let mut x = 10;
    let _y = X(&x);
    let _z = &mut x;
    Ok(())
}

しかし、 X<'a>Drop を実装した途端にコンパイルできなくなる。

impl<'a> Drop for X<'a> {
    fn drop(&mut self) { todo!() }
}
error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable
  --> src/main.rs:12:14
   |
11 |     let _y = X(&x);
   |                -- immutable borrow occurs here
12 |     let _z = &mut x;
   |              ^^^^^^ mutable borrow occurs here
13 |     Ok(())
14 | }
   | - immutable borrow might be used here, when `_y` is dropped and runs the `Drop` code for type `X`

理由は以下の stackoverflow のコメントに書いてある。 NLL は借用が本当に使われている箇所を特定するだけで、コードの意味を変えるものではない。特に、変数のライフタイムは変わらないので、 Drop トレイトを実装したデータ型を使うと、今までと同様に スコープが終了するタイミングで drop メソッドが呼ばれる 。これによって参照 &x が使われている範囲が伸びるので、コンパイルできなくなるのである。

rust - Will the non-lexical lifetime borrow checker release locks prematurely? - Stack Overflow