More than 1 year has passed since last update.
Rust 100 Ex 🏃【20/37】 動的配列のリサイズ・イテレータ ~またまたトレイト登場!~
前の記事
- 【0】 準備 ← 初回
- ...
- 【19】 配列・動的配列 ~スタックが使われる配列と、ヒープに保存できる動的配列~ ← 前回
- 【20】 動的配列のリサイズ・イテレータ ~またまたトレイト登場!~ ← 今回
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 を再現しているクレートがあるみたいです。
では次の問題に行きましょう!
Register as a new user and use Qiita more conveniently
- You get articles that match your needs
- You can efficiently read back useful information
- You can use dark theme
