できない.dev

Rust で「borrow of moved value」(E0382) が解決できない

所有権が別の変数や関数へ移った後に元の変数を使うと E0382 になる。
参照 & を渡す、clone() で複製する、Copy を実装するのいずれかで解決する。
ループ内での move も同じ理由で起きる。

公開: 更新:

実行例あり(2026-09-11 に実環境で検証)

要約

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 の例では未使用フィールドの警告が出るだけで p1p2 が別々の値として出力された。

$ 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`.
終了コード: 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 時点の出力

検証環境

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

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

よくある原因

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

解決策

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 を選ぶ。

この記事は役立ちましたか?