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]]