Rustエラー[E0382]: use of moved value を修正する — 所有権とムーブセマンティクス

intermediate🦀 Rust2026-03-23| Rust 1.60以降、全プラットフォーム(Linux、macOS、Windows)、rustcコンパイラ

Error Message

error[E0382]: use of moved value
#rust#所有権#ムーブ#clone

TL;DR

別の変数や関数にムーブされた後の値を使おうとしています。Rustの所有権システムは、ムーブ後に元のバインディングを無効化します。2つの簡単な修正方法:ムーブ前に.clone()を呼ぶか、値そのものの代わりに参照(&value)を渡すかです。

// エラーのコード
let s = String::from("hello");
let s2 = s;         // ここでsがムーブされる
println!("{}", s); // error[E0382]: use of moved value: `s`

// 修正1: clone
let s = String::from("hello");
let s2 = s.clone();
println!("{}", s); // OK

// 修正2: 借用
let s = String::from("hello");
let s2 = &s;
println!("{}", s); // OK

実際に何が起きているか

Rustにおけるヒープに確保された値は、常にただ1つの所有者を持ちます。s2 = sと代入したり、sを関数に渡したりすると、所有権が移転します。Rustではこれをムーブと呼びます。ムーブ後、sは消滅します。コンパイラはゴーストの使用を許しません。

エラーの全体像はこのようになります:

error[E0382]: use of moved value: `s`
 --> src/main.rs:4:20
  |
2 |     let s2 = s;
  |              - value moved here
3 |     println!("{}", s);
  |                    ^ value used here after move
  |
  = note: move occurs because `s` has type `String`, which does not implement the `Copy` trait

最後の行が重要です。Copyを実装している型——整数、真偽値、文字、Copy型のタプルなど——は、ムーブではなく暗黙的にコピーされます。StringVecHashMap、そして大半のヒープ型はCopyを実装していないため、ムーブになります。

よくあるシナリオと修正方法

シナリオ1:関数へのムーブ

fn save_to_db(name: String) { /* nameを消費する */ }

let name = String::from("Alice");
save_to_db(name);        // nameがsave_to_dbにムーブされる
println!("{}", name);   // error[E0382]: use of moved value: `name`

修正A: 所有権を持つ代わりに借用するよう関数シグネチャを変更する:

fn save_to_db(name: &str) { /* nameを借用する */ }

save_to_db(&name);
println!("{}", name); // OK

修正B: 関数を変更できない場合は、渡す前にcloneする:

save_to_db(name.clone());
println!("{}", name); // OK

シナリオ2:ループ内でのムーブ

深夜2時によく踏む罠です:

let data = vec![1, 2, 3];
for _ in 0..3 {
    process(data); // error[E0382]: use of moved value: `data`
                   // 最初のイテレーションでムーブされ、以降は消滅
}

fn process(v: Vec<i32>) { /* vを消費する */ }

修正A: 関数が所有権を必要としない場合は参照を渡す:

fn process(v: &[i32]) { /* vを借用する */ }

for _ in 0..3 {
    process(&data); // OK
}

修正B: 関数が値の所有権を必要とする場合のみ、ループ内でcloneする:

for _ in 0..3 {
    process(data.clone());
}

シナリオ3:クロージャへのムーブ

let config = Config::new();
let handler = move || {
    println!("{:?}", config); // configがクロージャにムーブされる
};
println!("{:?}", config); // error[E0382]: use of moved value

修正: moveクロージャにキャプチャされる前にcloneして、それぞれが独自のコピーを持つようにする:

let config = Config::new();
let config_clone = config.clone();
let handler = move || {
    println!("{:?}", config_clone);
};
println!("{:?}", config); // OK

シナリオ4:構造体フィールドの部分ムーブ

struct User {
    name: String,
    email: String,
}

let user = User {
    name: String::from("Alice"),
    email: String::from("alice@example.com"),
};

let name = user.name;       // 部分ムーブ!
println!("{}", user.email); // OK — emailはムーブされていない
println!("{}", user.name);  // error[E0382]: partial move

修正: 構造体を破壊しないようフィールドをcloneする:

let name = user.name.clone();
println!("{}", user.name); // OK

シナリオ5:matchによる値の消費

let opt = Some(String::from("data"));
match opt {
    Some(s) => println!("Got: {}", s), // sがoptからムーブされる
    None => {}
}
println!("{:?}", opt); // error[E0382]

修正: 参照にマッチさせる——optの前に&を追加する:

match &opt {
    Some(s) => println!("Got: {}", s),
    None => {}
}
println!("{:?}", opt); // OK

clone()がコストに見合わない場合

ホットループで100MBのVecをcloneするとパフォーマンスが著しく低下します。.clone()に頼る前に、以下の代替手段を検討してください:

  • Arc — スレッド間での共有所有権。cloneしてもアトミックカウンタをインクリメントするだけで、データはコピーされません。
  • Rc — 同じ考え方で、シングルスレッド専用。
  • Cow — 変更が必要になるまで借用し、その時点で遅延cloneします。
use std::sync::Arc;

let data = Arc::new(vec![1, 2, 3]);
let data2 = Arc::clone(&data); // ポインタの安価なclone、データのコピーではない

std::thread::spawn(move || {
    println!("{:?}", data2);
});
println!("{:?}", data); // OK

確認方法

修正を適用したら、E0382が解消されているか確認します:

cargo build 2>&1 | grep "E0382"

出力がなければ問題ありません。完全にクリーンな再ビルドを行う場合:

cargo clean && cargo build

Clippyも実行してみてください——手動cloneより慣用的な解決策を提案してくれることがよくあります:

cargo clippy

判断ガイド

  • 関数が値を読むだけの場合 → &Tを渡す
  • 関数が値を変更する必要がある場合 → &mut Tを渡す
  • 関数が所有権を必要とし、かつ元の値も使いたい場合 → .clone()
  • スレッド間で値を共有する場合 → Arc<T>
  • シングルスレッドのコードで値を共有する場合 → Rc<T>
  • 型が小さくスタックのみに存在する場合 → Copyを導出する
#[derive(Copy, Clone)]
struct Point { x: f64, y: f64 }

let p = Point { x: 1.0, y: 2.0 };
let p2 = p; // ムーブではなくコピー
println!("{}", p.x); // OK

Related Error Notes