https://doc.rust-jp.rs/book-ja/ch17-00-oop.html - [[👤Alan Kay]] ## オブジェクト指向言語の特徴 ```rust pub struct AverageCollection { list: Vec<i32>, average: f64, } impl AverageCollection { pub fn add(&mut self, value: i32) { self.list.push(value); self.update_average(); } pub fn remove(&mut self) -> Option<i32> { let result = self.list.pop(); match result { Some(value) => { self.update_average(); Some(value) } None => None, } } pub fn average(&self) -> f64 { self.average } fn update_average(&mut self) { let total: i32 = self.list.iter().sum(); self.average = total as f64 / self.list.len() as f64; } } fn main() { let mut av = AverageCollection { list: vec![], average: 0.0, }; av.add(10); av.add(20); av.add(30); av.remove(); eprintln!("av.average() = {:?}", av.average()); } ``` ## [[トレイトオブジェクト]]で異なる型の値を許容する - [[トレイトオブジェクト]] - [[ジェネリクス (Rust)|ジェネリクス]]と[[トレイト境界]]を使う場合は、型`T`はすべて同一の型になる必要がある - つまり `Vec<T>` は2つ以上の型を混同できない - 一方 `Vec<Box<Draw>>`のような表現なら、`Draw`[[トレイト]]の[[トレイトオブジェクト]]であれば2つ以上の型を混同できる `Screen`は[[dyn]]をつけないと怒られる。 ```rust pub struct Screen { pub components: Vec<Box<dyn Draw>>, } ``` - [[トレイトオブジェクトはdynキーワードを含まなければいけない]] `lib.rs` ```rust pub trait Draw { fn draw(&self); } pub struct Screen { pub components: Vec<Box<dyn Draw>>, } impl Screen { pub fn run(&self) { for component in self.components.iter() { component.draw(); } } } pub struct Button { pub width: u32, pub height: u32, pub label: String, } impl Draw for Button { fn draw(&self) { println!("[{}] ({}x{})", self.label, self.width, self.height); } } ``` `main.rs` ```rust use trait_obj_sample::{Button, Draw, Screen}; struct SelectBox { width: u32, height: u32, options: Vec<String>, } impl Draw for SelectBox { fn draw(&self) { println!("▼ ({}x{})", self.width, self.height); self.options.iter().for_each(|x| { println!("- {}", x); }) } } fn main() { let screen = Screen { components: vec![ Box::new(SelectBox { width: 75, height: 10, options: vec![ String::from("Yes"), String::from("Maybe"), String::from("No"), ], }), Box::new(Button { width: 50, height: 10, label: String::from("OK"), }), ], }; screen.run(); } ``` - [[単相化]]... 完全に忘れてた - [[静的ディスパッチ]]と[[動的ディスパッチ]] - [[トレイトオブジェクト]]を使用すると[[動的ディスパッチ]]が使われる - [[トレイトオブジェクト]]の[[オブジェクト安全]] ## オブジェクト指向デザインパターンを実装する `take`を使う理由が分からん。。 ```rust #[derive(Debug)] struct Id { value: i32, } #[derive(Debug)] struct Human { pub id: Option<Box<Id>>, pub name: String, } fn main() { let mut ichiro = Human { id: Some(Box::new(Id { value: 51 })), name: "Ichiro".to_string(), }; if let Some(id) = ichiro.id { ichiro.id = Some(Box::new(Id { value: id.value + 1, })); }; eprintln!("ichiro.id.unwrap() = {:?}", ichiro.id.unwrap()); } ``` - 試しにコード書いてみたけど、これも動くしなあ... - [[トレイトオブジェクト]]だけで起こる現象なのか? - [[Option (Rust)|Option]]内部への参照が欲しいので[[as_ref]]