Code Fix

中級

swap_remove は要素の順序を保証しない(remove との違い)

Vecのswap_removeは指定位置の要素を削除し、その穴を最後の要素で埋めるため高速ですが、要素の順序が変わります。順序を保ったまま削除したい場合はremoveを使う必要があります。

なぜエラーが出ないのか

出力: [1, 5, 3, 4]
(エラーなし)
Rustは何も報告しません。型としても借用としても正しいコードだからです
出力: [1, 5, 3, 4]
実際の出力 — 期待した結果と食い違っている箇所
見つけ方
エラーが出ないので、出力を目で確かめるしかありません。この種の誤りが最も発見が遅れます

このエラーが出る典型パターン

パターン1

 1  fn main() {
 2      let mut nums = vec![1, 2, 3, 4, 5];
 3      nums.swap_remove(1);
                  ^
 4      println!("{:?}", nums);
 5  }
出力: [1, 5, 3, 4]

swap_removeは削除した穴を最後の要素で埋めるため順序が変わります。順序を保ちたい場合はremoveを使います。

直し方: swap_removeremove にします。

この問題を解いてみる →

広告
広告スロット(未設定)

パターン2

 1  fn main() {
 2      let mut queue = vec![10, 20, 30, 40];
 3      queue.swap_remove(0);
                   ^
 4      println!("{:?}", queue);
 5  }
出力: [40, 20, 30]

先頭の要素を削除する場合、swap_removeでは最後の要素が先頭に来てしまい順序が崩れます。

直し方: swap_removeremove にします。

この問題を解いてみる →

パターン3

 1  fn main() {
 2      let mut ranking = vec!["a", "b", "c", "d"];
 3      ranking.swap_remove(1);
                     ^
 4      println!("{:?}", ranking);
 5  }
出力: ["a", "d", "c"]

ランキングのように順序に意味があるデータでは、swap_removeを使うと結果が壊れてしまいます。

直し方: swap_removeremove にします。

この問題を解いてみる →

パターン4

 1  fn main() {
 2      let mut steps = vec![1, 2, 3, 4, 5, 6];
 3      steps.swap_remove(2);
                   ^
 4      println!("{:?}", steps);
 5  }
出力: [1, 2, 6, 4, 5]

swap_removeはO(1)で高速ですが、順序を保証しないというトレードオフがあります。

直し方: swap_removeremove にします。

この問題を解いてみる →

パターン5

 1  fn main() {
 2      let mut playlist = vec!["song1", "song2", "song3"];
 3      playlist.swap_remove(0);
                      ^
 4      println!("{:?}", playlist);
 5  }
出力: ["song3", "song2"]

順序を保つ必要がある場面では、多少コストがかかってもremoveを選ぶ必要があります。

直し方: swap_removeremove にします。

この問題を解いてみる →

よくある誤解

「removeもswap_removeも、指定した位置の要素を取り除くだけの同じ操作のはず」という思い込みは誤りです。swap_removeは削除のたびに最後の要素を穴埋めに使うため、以降の要素の並び順が入れ替わります。

まとめ

swap_remove は要素の順序を保証しない(remove との違い)は中級でつまずきやすい項目です。上の5パターンを実際に手で直すと、エラーメッセージのどこを読めばよいかが掴めます。

演習をはじめる

関連するエラー

広告
広告スロット(未設定)