More than 1 year has passed since last update.
Rust 100 Ex 🏃【25/37】 インデックス・可変インデックス ~インデックスもトレイト!~
前の記事
- 【0】 準備 ← 初回
- ...
- 【24】 可変スライス・下書き構造体 ~構造体で状態表現~ ← 前回
- 【25】 インデックス・可変インデックス ~インデックスもトレイト!~ ← 今回
100 Exercise To Learn Rust 演習第25回になります!
今回の関連ページ
[06_ticket_management/13_index] インデックスアクセス
問題はこちらです。
// TODO: Implement `Index<&TicketId>` and `Index<TicketId>` for `TicketStore`.
use ticket_fields::{TicketDescription, TicketTitle};
#[derive(Clone)]
pub struct TicketStore {
tickets: Vec<Ticket>,
counter: u64,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct TicketId(u64);
#[derive(Clone, Debug, PartialEq)]
pub struct Ticket {
pub id: TicketId,
pub title: TicketTitle,
pub description: TicketDescription,
pub status: Status,
}
#[derive(Clone, Debug, PartialEq)]
pub struct TicketDraft {
pub title: TicketTitle,
pub description: TicketDescription,
}
#[derive(Clone, Debug, Copy, PartialEq)]
pub enum Status {
ToDo,
InProgress,
Done,
}
impl TicketStore {
pub fn new() -> Self {
Self {
tickets: Vec::new(),
counter: 0,
}
}
pub fn add_ticket(&mut self, ticket: TicketDraft) -> TicketId {
let id = TicketId(self.counter);
self.counter += 1;
let ticket = Ticket {
id,
title: ticket.title,
description: ticket.description,
status: Status::ToDo,
};
self.tickets.push(ticket);
id
}
pub fn get(&self, id: TicketId) -> Option<&Ticket> {
self.tickets.iter().find(|&t| t.id == id)
}
}
#[cfg(test)]
mod tests {
use crate::{Status, TicketDraft, TicketStore};
use ticket_fields::test_helpers::{ticket_description, ticket_title};
#[test]
fn works() {
let mut store = TicketStore::new();
let draft1 = TicketDraft {
title: ticket_title(),
description: ticket_description(),
};
let id1 = store.add_ticket(draft1.clone());
let ticket1 = &store[id1];
assert_eq!(draft1.title, ticket1.title);
assert_eq!(draft1.description, ticket1.description);
assert_eq!(ticket1.status, Status::ToDo);
let draft2 = TicketDraft {
title: ticket_title(),
description: ticket_description(),
};
let id2 = store.add_ticket(draft2);
let ticket2 = &store[&id2];
assert_ne!(id1, id2);
}
}
Index<TicketId> トレイトと、 Index<&TicketId> トレイトを TicketStore に実装せよ!という問題ですね。
テスト中に、配列ではなく構造体に対して配列のようにアクセスしている
let ticket1 = &store[id1];
みたいな記述があります。すなわち今回扱う Index トレイトは [] による要素アクセスを実現するものというわけです1!
解説
素直に実装してしまえばよいです。Bookで言及されているように、 Index トレイトは [] の中に入ってくる型(問題だと TicketId または &TicketId )を Index<Idx> の Idx としてジェネリクスパラメータで、得られる参照の要素の型(問題だと Ticket)を Output 関連型で指定するようになっています。 Idx が決まれば Output も決まる、という写像のような関係になっていますね。
use std::ops::Index;
impl Index<TicketId> for TicketStore {
type Output = Ticket;
fn index(&self, index: TicketId) -> &Self::Output {
self.get(index).unwrap()
}
}
impl Index<&TicketId> for TicketStore {
type Output = Ticket;
fn index(&self, index: &TicketId) -> &Self::Output {
self.get(*index).unwrap()
}
}
get メソッドは範囲外アクセスの時は None を返してくれますが、 index メソッドでは折角返してくれた Option 型を unwrap しています。Rustでは [] による要素アクセスは範囲外の時はパニックするというのが慣例なので問題ないです。 Option を返すようにしてほしい時は、スライスのメソッド get と同様に get メソッドとして提供するのが慣例です。
[06_ticket_management/14_index_mut] 可変インデックスアクセス
問題として用意されているソースコードはほとんど変わりません。可変インデックスに関するテストが追加されたのみです。
解説
問題がほとんど変わらないということは、つまり回答もほぼ変わらないということですね!シンプルに実装しています。
use std::ops::IndexMut;
impl IndexMut<TicketId> for TicketStore {
// <TicketStore as Index>::Outputは一意に決まるからSelf::Outputで取得できる
fn index_mut(&mut self, index: TicketId) -> &mut Self::Output {
self.tickets.iter_mut().find(|t| t.id == index).unwrap()
}
}
impl IndexMut<&TicketId> for TicketStore {
fn index_mut(&mut self, index: &TicketId) -> &mut Self::Output {
&mut self[*index]
}
}
なお、関連型の Output の指定がありません。これは IndexMut<Idx> がトレイト境界に Index<Idx> を要求しているからで、 Self::Output の型は Index<Idx> で指定した型と同一のものが入っています!
では次の問題に行きましょう!
次の記事: 【26】 HashMap・順序・BTreeMap ~Rustの辞書型~
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
