error[E0506]: cannot assign to `var` because it is borrowed

参照中の変数への書込を行った。

注意点

このエラー自体のルールは単純である。

しかし、エラーを読み解くには、参照まわりのその他の知識も必要になる事がある。

例えば、『ライフタイムの延長』の影響があると、初見では納得しにくいエラーになる。

メッセージ

メッセージの構成はだいたい以下のようになる。

要約部
  • 参照中の書込が検出された変数名
詳細部
  • 参照の発生個所 ← エラー個所より前方
  • 書込の発生個所 ← エラー個所
  • 参照の使用箇所 ← エラー個所より後方

パターン

パターン A

基本形

最も単純なパターン。

サンプル


fn main() {
    let mut var = 0;
    let var_ref = &var;
    var = 1;
    println!("{var}, {var_ref}");
}

error[E0506]: cannot assign to `var` because it is borrowed
 --> src\main.rs:4:5
  |
3 |     let var_ref = &var;
  |                   ---- `var` is borrowed here
4 |     var = 1;
  |     ^^^^^^^ `var` is assigned to here but it was already borrowed
5 |     println!("{var}, {var_ref}");
  |                       ------- borrow later used here

パターン B1

Drop トレイトの影響 (通常版)

詳しくは『Drop トレイトのライフタイムへの影響 - ライフタイムの延長』を参照。
(注: Drop トレイトを直接的に扱っていなくても考慮が必要。)

サンプル


fn main() {
    let mut var = 1;
    let _refs = MyRef(&var);
    var = 2;
    dbg!(var);
}

struct MyRef<'a>(&'a i32);
impl Drop for MyRef<'_> {
    fn drop(&mut self) {
        println!("ID [{}] is drpoped.", self.0)
    }
}

error[E0506]: cannot assign to `var` because it is borrowed
 --> src\main.rs:4:5
  |
3 |     let _refs = MyRef(&var);
  |                       ---- `var` is borrowed here
4 |     var = 2;
  |     ^^^^^^^ `var` is assigned to here but it was already borrowed
5 |     dbg!(var);
6 | }
  | - borrow might be used here, when `_refs` is dropped and runs the `Drop` code for type `MyRef`

パターン B2

Drop トレイトの影響 (複数の文での自己更新)

これは前のパターンの特殊版で、Drop トレイトでライフタイムが延長された参照元が残った状態で、その参照元を更新すると発生する。

サンプル


fn main() {
    let mut var = MyType(1);
    let refs = var.refs();
    var = refs.new_value();
    dbg!(var.0);
}

struct MyType(i32);

impl MyType {
    fn refs(&self) -> MyRef<'_> {
        MyRef(&self.0)
    }
}

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

impl MyRef<'_> {
    fn new_value(&self) -> MyType {
        MyType(self.0 + 1)
    }
}

impl Drop for MyRef<'_> {
    fn drop(&mut self) {
        println!("ID [{}] is drpoped.", self.0);
    }
}

error[E0506]: cannot assign to `var` because it is borrowed
 --> src\main.rs:4:5
  |
3 |     let refs = var.refs();
  |                --- `var` is borrowed here
4 |     var = refs.new_value();
  |     ^^^^^^^^^^^^^^^^^^^^^^ `var` is assigned to here but it was already borrowed
5 |     dbg!(var.0);
6 | }
  | - borrow might be used here, when `refs` is dropped and runs the `Drop` code for type `MyRef`

解決策

更新値の導出と設定を分けると良い。


fn main() {
    let mut var = MyType(1);
    let refs = var.refs();
    let new_value = refs.new_value();
    drop(refs);
    var = new_value;
    dbg!(var.0);
}

struct MyType(i32);

impl MyType {
    fn refs(&self) -> MyRef<'_> {
        MyRef(&self.0)
    }
}

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

impl MyRef<'_> {
    fn new_value(&self) -> MyType {
        MyType(self.0 + 1)
    }
}

impl Drop for MyRef<'_> {
    fn drop(&mut self) {
        println!("ID [{}] is drpoped.", self.0);
    }
}

パターン B3

Drop トレイトの影響 (一つの文での自己更新)

これは前のパターンの特殊版で、問題となる処理が一文に短縮されている。これにより、文内での一時的な値が絡むため、メッセージが少しだけ変化する。

サンプル


fn main() {
    let mut var = MyType(1);
    var = var.refs().new_value();
    dbg!(var.0);
}

struct MyType(i32);

impl MyType {
    fn refs(&self) -> MyRef<'_> {
        MyRef(&self.0)
    }
}

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

impl MyRef<'_> {
    fn new_value(&self) -> MyType {
        MyType(self.0 + 1)
    }
}

impl Drop for MyRef<'_> {
    fn drop(&mut self) {
        println!("ID [{}] is drpoped.", self.0);
    }
}

error[E0506]: cannot assign to `var` because it is borrowed
 --> src\main.rs:3:5
  |
3 |     var = var.refs().new_value();
  |     ^^^^^^----------^^^^^^^^^^^^- ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `MyRef`
  |     |     |
  |     |     `var` is borrowed here
  |     |     a temporary with access to the borrow is created here ...
  |     `var` is assigned to here but it was already borrowed

解決策

前のパターンと同様、更新値の導出と設定を分けると良い。


fn main() {
    let mut var = MyType(1);
    let new_value = var.refs().new_value();
    var = new_value;
    dbg!(var.0);
}

struct MyType(i32);

impl MyType {
    fn refs(&self) -> MyRef<'_> {
        MyRef(&self.0)
    }
}

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

impl MyRef<'_> {
    fn new_value(&self) -> MyType {
        MyType(self.0 + 1)
    }
}

impl Drop for MyRef<'_> {
    fn drop(&mut self) {
        println!("ID [{}] is drpoped.", self.0);
    }
}