
【AI文芸 シリーズRustの教科書】retain() - 「削除」ではなく、「残す」視点で考える。
夜のオフィス
時計の針は午後10時を回っていた。
誰もいないオフィスで、彼はひとりコードを書いていた。
カレンダーアプリの改良に取り組んでいたのだが、予定の削除処理がどうにもスッキリしない。
ループを使った削除処理が、どこか不格好に思えていた。
そんなとき、背後から静かな足音が聞こえた。
「……まだやってるの?」
振り返ると、玲奈様がいた。
夜のオフィスに似合う、落ち着いたトーンのスーツを身にまとい、手には温かいコーヒー。
彼は苦笑しながら、モニターを指差す。「この delete_schedule() の部分なんですけど、retain() を使ったほうがいいって言われたんですが……」
玲奈様は静かに彼の席の横に立ち、画面を覗き込んだ。
「……ふふっ、また無駄なことをしてるわね」
1. retain() を使えば、ループは不要よ
玲奈様は、彼が書いたコードを指差した。
for i in 0..calendar.schedules.len() {
if calendar.schedules[i].id == id {
calendar.schedules.remove(i);
return true;
}
}
「……remove(i) を使って要素を削除すると、ループ中に Vec<T> の要素がずれてしまう危険があるわ」
「え?」
「それに、remove() を使うと、一つずつ削除するたびにメモリの再配置が発生する可能性がある。それって、効率的だと思う?」
彼は息を飲んだ。「……じゃあ、どうすれば?」
玲奈様は、静かに retain() を使ったコードを書き加えた。
pub fn delete_schedule(calendar: &mut Calendar, id: u64) -> bool {
let original_len = calendar.schedules.len();
calendar.schedules.retain(|schedule| schedule.id != id);
original_len != calendar.schedules.len()
}
「retain() を使えば、「残したい要素」だけをフィルタリングできるの」
彼は目を見開いた。「……ループも remove() もいらない……?」
「ええ。クロージャを使うだけで、簡潔に書けるわ」
2. retain() の基本 – 残したいものだけを選びなさい
「retain() の仕組みは簡単よ。true を返した要素だけを残し、false を返した要素は削除されるの」
玲奈様は、簡単な例を見せた。
fn main() {
let mut numbers = vec![1, 2, 3, 4, 5];
// 偶数だけを残す
numbers.retain(|&x| x % 2 == 0);
println!("{:?}", numbers); // [2, 4]
}
彼は納得したように頷く。「削除するんじゃなくて、残すものを選ぶんですね」
「そういうこと。削除のロジックじゃなく、残すべきものに意識を向けなさい」
3. retain() なら複雑な条件でも簡単よ
「それに retain() は、単純な削除だけじゃなくて、複雑な条件のフィルタリングにも使えるの」
玲奈様は、さらに例を見せた。
fn main() {
let mut events = vec![
("会議", 60),
("ランチ", 30),
("プレゼン", 90),
("読書", 120),
];
// 60分未満のイベントを削除
events.retain(|&(_, duration)| duration >= 60);
println!("{:?}", events);
// [("会議", 60), ("プレゼン", 90), ("読書", 120)]
}
彼は興味深そうに画面を見つめる。「これなら、条件を変えるだけで柔軟に削除できますね!」
「そうよ。retain() を使えば、無駄なループを書かずに済むの」
4. retain() vs filter() の違い
「でも、filter() でも似たようなことができますよね?」
玲奈様は、静かに首を振った。
「filter() は新しい Vec<T> を作るのに対し、retain() は既存の Vec<T> を変更するの」
彼は少し考え込んだ。「……つまり、メモリの再割り当てが減る?」
玲奈様は満足げに頷いた。
let numbers = vec![1, 2, 3, 4, 5];
// `filter()` を使うと新しい `Vec<T>` ができる
let evens: Vec<i32> = numbers.into_iter().filter(|x| x % 2 == 0).collect();
// `retain()` は元の `Vec<T>` を変更する
let mut numbers = vec![1, 2, 3, 4, 5];
numbers.retain(|x| x % 2 == 0);
「retain() はメモリのコピーを減らして、パフォーマンスを向上させることができるの」
「なるほど……じゃあ、元の Vec<T> をそのまま使いたいときは retain()、新しい Vec<T> を作りたいときは filter() ですね!」
5. retain() を使いこなせば、無駄なコードが減るわ
彼は、自分のコードを見直して、ため息をついた。
「……今まで、何も考えずに remove() を使ってました」
玲奈様は静かに笑う。「大切なのは、より良い方法を知ることよ」
「コードは未来の自分へのメッセージ。シンプルに、分かりやすく、無駄をなくしなさい」
彼はノートを開き、ゆっくりと retain() についてのメモを書き加えた。
「……ありがとうございます、玲奈様」
玲奈様は微笑みながら、時計をちらりと見る。
「もう遅いわね。帰る準備は?」
彼は軽く伸びをしながら、PCを閉じた。「……そうですね、そろそろ帰ります」
玲奈様は静かに立ち上がり、夜のオフィスを後にした。
彼はデスクに座ったまま、しばらく retain() のコードを思い返していた。
「削除」ではなく、「残す」視点で考える。
これからのコーディングスタイルが、少しだけ変わる気がした。
こうして彼のコードは、よりシンプルに、より効率的に進化した。
玲奈様の教えは、また一つ、彼の中に刻まれた。