できない.dev

Rust「borrow of moved value」の原因と直し方

borrow of moved value(E0382)は、参照 & を渡すか clone() で複製すれば直る。
所有権が移った後の変数を使うと出る。
軽い自作型は Copy を実装すれば move しない。

公開: 更新:

症状できない · exit 1
rustc bad.rs
…
error[E0382]: borrow of moved value: `s1`
 --> bad.rs:4:23
  |
2 |     let s1 = String::from("hello");

2026-09-11 に rust:1(Python 3.13.5 / Git 2.47.3) で実際に打って取った出力。検証環境の詳細

要約

borrow of moved value(E0382)は、所有権が移った後の変数を使ったときに出る。
Rust では Rc のような仕組みを使わない限り、1 つの値を 2 つの変数が同時に所有することはできない。

let s1 = String::from("hello");
let s2 = s1;          // ここで所有権が s2 へ移る
println!("{}", s1);   // error[E0382]: borrow of moved value: `s1`

対処は三択である。
所有権を渡さない(参照)、別の値を作る(clone)、そもそも move させない(Copy)。
用途に合うものを選ぶ。

実行例

上記の手順を rust:1 で動かすと、代入で所有権が移った s1 を使うコードは E0382 でコンパイルが止まり、コンパイラ自身も clone() を付ける修正を提案した。
参照・clone()・Copy・参照での反復の 4 通りは、いずれもコンパイルが通った。Copy の例では未使用フィールドの警告が出るだけで、p1 と p2 が別々の値として出力された。

$ rustc bad.rs
error[E0382]: borrow of moved value: `s1`
 --> bad.rs:4:23
  |
2 |     let s1 = String::from("hello");
  |         -- move occurs because `s1` has type `String`, which does not implement the `Copy` trait
3 |     let s2 = s1; // ここで所有権が s2 へ移る
  |              -- value moved here
4 |     println!("{} {}", s1, s2);
  |                       ^^ value borrowed here after move
  |
help: consider cloning the value if the performance cost is acceptable
  |
3 |     let s2 = s1.clone(); // ここで所有権が s2 へ移る
  |                ++++++++
 
error: aborting due to 1 previous error
 
For more information about this error, try `rustc --explain E0382`.
$ echo $?
1
$ rustc solve_ref.rs
$ ./solve_ref
hello 5
$ rustc solve_clone.rs
$ ./solve_clone
any many
$ rustc solve_copy.rs
warning: field `y` is never read
 --> solve_copy.rs:4:5
  |
2 | struct Point {
  |        ----- field in this struct
3 |     x: i32,
4 |     y: i32,
  |     ^
  |
  = note: `Point` has derived impls for the traits `Debug` and `Clone`, but these are intentionally ignored during dead code analysis
  = note: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default
 
warning: 1 warning emitted
$ ./solve_copy
Point { x: 1, y: 2 } Point { x: -1, y: 2 }
$ rustc solve_iter.rs
$ ./solve_iter
1
2
3
len: 3

— 2026-09-11 時点の出力

検証環境Python 3.13.5 / Git 2.47.3 / 2026-09-11 検証
検証日
実行環境
rust:1 Debian GNU/Linux 13 (trixie)
バージョン
  • Python 3.13.5
  • Git 2.47.3

この記事の「実行例」は、上記の環境で実際にコマンドを実行して得られた出力をそのまま掲載しています。 再現手順はリポジトリの検証スクリプトとして管理し、定期的に再実行して出力を更新しています。

解決策

1. 参照を渡す

fn calculate_length(s: &String) -> usize {
    s.len()
}
 
let s1 = String::from("hello");
let len = calculate_length(&s1);
println!("{} {}", s1, len);   // s1 は所有権を保ったまま

読み取るだけなら参照で足りる。所有権の章(新しいタブで開く)が扱う基本形である。

2. clone() で複製する

let mut s1 = String::from("many");
let s2 = s1.clone();
s1.remove(0);
println!("{} {}", s1, s2);   // any many

複製のコストを払う代わりに、以降は互いに独立した値として扱える。

3. 軽い自作型は Copy にする

#[derive(Copy, Clone)]
struct Point { x: i32, y: i32 }
 
let mut p1 = Point { x: -1, y: 2 };
let p2 = p1;   // move ではなくコピー
p1.x = 1;

全フィールドが Copy である型に限って付けられる。String を含む型には付けられない。

4. コレクションは参照で反復する

let v = vec![1, 2, 3];
for x in &v {
    println!("{}", x);
}
println!("{}", v.len());   // v はまだ生きている

書き換えたいなら &mut v を使う。
所有権ごと消費したいときだけ for x in v を選ぶ。

よくある原因

  1. 代入で move している。String や Vec のように Copy を持たない型は、代入した時点で元の変数が使えなくなる。
  2. 関数へ値渡ししている。
    引数の型が String なら所有権ごと渡る。
    呼び出し後に元の変数を使えば同じエラーになる。
  3. for が所有権ごと奪っている。for x in v は v を消費する。
    ループ後に v.len() を呼ぶと E0382 になる。
  4. クロージャが move で値を奪っている。thread::spawn などへ渡すクロージャは値を取り込むため、外側では使えなくなる。