前の記事
- 【0】 準備 ← 初回
- ...
- 【19】 配列・動的配列 ~スタックが使われる配列と、ヒープに保存できる動的配列~ ← 前回
- 【20】 動的配列のリサイズ・イテレータ ~またまたトレイト登場!~ ← 今回
全記事一覧
- 【0】 準備
- 【1】 構文・整数・変数
- 【2】 if・パニック・演習
- 【3】 可変・ループ・オーバーフロー
- 【4】 キャスト・構造体 (たまにUFCS)
- 【5】 バリデーション・モジュールの公開範囲 ~ → カプセル化!~
- 【6】 カプセル化の続きと所有権とセッター ~そして不変参照と可変参照!~
- 【7】 スタック・ヒープと参照のサイズ ~メモリの話~
- 【8】 デストラクタ(変数の終わり)・トレイト ~終わりと始まり~
- 【9】 Orphan rule (孤児ルール)・演算子オーバーロード・derive ~Empowerment 💪 ~
- 【10】 トレイト境界・文字列・Derefトレイト ~トレイトのアレコレ~
- 【11】 Sized トレイト・From トレイト・関連型 ~おもしろトレイトと関連型~
- 【12】 Clone・Copy・Dropトレイト ~覚えるべき主要トレイトたち~
- 【13】 トレイトまとめ・列挙型・match式 ~最強のトレイトの次は、最強の列挙型~
- 【14】 フィールド付き列挙型とOption型 ~チョクワガタ~
- 【15】 Result型 ~Rust流エラーハンドリング術~
- 【16】 Errorトレイトと外部クレート ~依存はCargo.tomlに全部お任せ!~
- 【17】 thiserror・TryFrom ~トレイトもResultも自由自在!~
- 【18】 Errorのネスト・慣例的な書き方 ~Rustらしさの目醒め~
- 【19】 配列・動的配列 ~スタックが使われる配列と、ヒープに保存できる動的配列~
- 【20】 動的配列のリサイズ・イテレータ ~またまたトレイト登場!~
- 【21】 イテレータ・ライフタイム ~ライフタイム注釈ようやく登場!~
- 【22】 コンビネータ・RPIT ~ 「
Iterator
トレイトを実装してるやつ」~ - 【23】
impl Trait
・スライス ~配列の欠片~ - 【24】 可変スライス・下書き構造体 ~構造体で状態表現~
- 【25】 インデックス・可変インデックス ~インデックスもトレイト!~
- 【26】 HashMap・順序・BTreeMap ~Rustの辞書型~
- 【27】 スレッド・'staticライフタイム ~並列処理に見るRustの恩恵~
- 【28】 リーク・スコープ付きスレッド ~ライフタイムに技あり!~
- 【29】 チャネル・参照の内部可変性 ~Rustの虎の子、mpscと
Rc<RefCell<T>>
~ - 【30】 双方向通信・リファクタリング ~返信用封筒を入れよう!~
- 【31】 上限付きチャネル・PATCH機能 ~パンクしないように制御!~
- 【32】
Send
・排他的ロック(Mutex
)・非対称排他的ロック(RwLock
) ~真打Arc<Mutex<T>>
登場~ - 【33】 チャネルなしで実装・Syncの話 ~考察回です~
- 【34】
async fn
・非同期タスク生成 ~Rustの非同期入門~ - 【35】 非同期ランタイム・Futureトレイト ~非同期のお作法~
- 【36】 ブロッキング・非同期用の実装・キャンセル ~ラストスパート!~
- 【37】 Axumでクラサバ! ~最終回~
- 【おまけ1】 Rustで勘違いしていたこと3選 🏄🌴 【100 Exercises To Learn Rust 🦀 完走記事 🏃】
- 【おまけ2】 【🙇 懺悔 🙇】Qiitanグッズ欲しさに1日に33記事投稿した話またはQiita CLIとcargo scriptを布教する的な何か
100 Exercise To Learn Rust 演習第20回になります!
今回の関連ページ
[06_ticket_management/03_resizing] Vec::new
と Vec::with_capacity
問題はこちらです。
#[cfg(test)]
mod tests {
#[test]
fn resizing() {
let mut v = Vec::with_capacity(2);
v.push(1);
v.push(2); // max capacity reached
assert_eq!(v.capacity(), 2);
v.push(3); // beyond capacity, needs to resize
// Can you guess what the new capacity will be?
// Beware that the standard library makes no guarantees about the
// algorithm used to resize the vector, so this may change in the future.
assert_eq!(v.capacity(), todo!());
}
}
本編解説は、「動的配列のサイズが予めわかっているなら、リサイズが起きない Vec::with_capacity
で動的配列を作成しよう!」という内容で、それに関連して、「動的配列のリサイズが起きてしまった場合、どれぐらいのサイズになるか想像できる?」というクイズになっています!
解説
システムの気持ちなんてわからん!!ってことでテキトーに値を代入して解きました。
assert_eq!(v.capacity(), 4); // わからんかった!!!
自分の環境だと4でした。環境に依存しそうな感じがしますね。
[06_ticket_management/04_iterators] イテレータ 1 ( IntoIterator
トレイト )
問題はこちらです。
use ticket_fields::{TicketDescription, TicketTitle};
// TODO: Let's start sketching our ticket store!
// First task: implement `IntoIterator` on `TicketStore` to allow iterating over all the tickets
// it contains using a `for` loop.
//
// Hint: you shouldn't have to implement the `Iterator` trait in this case.
#[derive(Clone)]
pub struct TicketStore {
tickets: Vec<Ticket>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Ticket {
pub title: TicketTitle,
pub description: TicketDescription,
pub status: Status,
}
#[derive(Clone, Debug, Copy, PartialEq)]
pub enum Status {
ToDo,
InProgress,
Done,
}
impl TicketStore {
pub fn new() -> Self {
Self {
tickets: Vec::new(),
}
}
pub fn add_ticket(&mut self, ticket: Ticket) {
self.tickets.push(ticket);
}
}
#[cfg(test)]
mod tests {
use super::*;
use ticket_fields::test_helpers::{ticket_description, ticket_title};
#[test]
fn add_ticket() {
let mut store = TicketStore::new();
let ticket = Ticket {
title: ticket_title(),
description: ticket_description(),
status: Status::ToDo,
};
store.add_ticket(ticket);
let ticket = Ticket {
title: ticket_title(),
description: ticket_description(),
status: Status::InProgress,
};
store.add_ticket(ticket);
let tickets: Vec<_> = store.clone().into_iter().collect();
assert_eq!(tickets, store.tickets);
}
}
解説
本エクササイズのBookは「 for
文は loop
式の中で Iterator::next
などを呼び出しているに過ぎない」と言った中々面白いことが書かれているので是非読んでみてください。(というか全ページ嫁!)
要約すると、「 for item in ... {}
の ...
には IntoIterator
、つまり「イテレータに変換する」トレイトを持つ構造体ならなんでも指定できますよ!」ということが書かれており、そのことを理解するために IntoIterator
を実装してみるという問題になっています。(でもテストに for
文出てこなくない...?)
impl IntoIterator for TicketStore {
type Item = Ticket;
type IntoIter = <Vec<Ticket> as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.tickets.into_iter()
}
}
IntoIterator
トレイトを実装することで、次のように書くことが可能になります!
let mut store = TicketStore::new();
// ...storeにTicket追加...
for ticket in store {
dbg!(ticket);
}
演算子オーバーロードに近い感じですかね...今回もトレイトのお陰で構文が説明しやすくなっており、「 for item in ... {}
の ...
には 0..5
や配列を指定できるよ」みたいな曖昧な説明は要らず、「 IntoIterator
を実装している構造体を指定可能だよ!」とハッキリ言えるという良さがあります!
IntoIterator
を実装している構造体として面白いものに、後ほどのエクササイズで登場する mpsc::Receiver
があります。この構造体はスレッド間で値を送り合うチャネル機能を持っているのですが、 Receiver
を for
文にわたすと、チャネルが閉じられるまでイテレートしてくれるという面白い書き方が可能になる感じです。
ジェネレータ構文は今のところない
上記に関連していそうなこととして、stable Rustには今のところ「ジェネレータ」はありません。いわゆる「 yield
文によって途中で値を生み出して一旦停止する関数」ですね...Pythonではおなじみのやつです。 代わりに何かしら構造体を用意して、今回みたいに IntoIterator
トレイトや Iterator
トレイトを実装してしまえば似たようなことが実現可能であるため、用意していないのではないかなと思います。それはそれとして利便性的に欲しくはあるので、 nightlyでは実験機能として提供 (もともとGeneratorと呼ばれていましたが、Coroutineに改名されたようです。後これとはまた違ったイテレータ生成向け構文も検討されているらしい...?) されていたり、マクロで yield
を再現しているクレートがあるみたいです。
では次の問題に行きましょう!