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_remove を remove にします。
広告
広告スロット(未設定)
パターン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_remove を remove にします。
パターン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_remove を remove にします。
パターン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_remove を remove にします。
パターン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_remove を remove にします。
よくある誤解
「removeもswap_removeも、指定した位置の要素を取り除くだけの同じ操作のはず」という思い込みは誤りです。swap_removeは削除のたびに最後の要素を穴埋めに使うため、以降の要素の並び順が入れ替わります。
まとめ
swap_remove は要素の順序を保証しない(remove との違い)は中級でつまずきやすい項目です。上の5パターンを実際に手で直すと、エラーメッセージのどこを読めばよいかが掴めます。