https://doc.rust-jp.rs/book-ja/ch10-00-generics.html ## ジェネリックなデータ型 - `T`は"type"の省略形 - `fn hoge<T>(list: &[T]) -> T {...}` コンパイルエラーを自力で解決する。 ```rust fn largest<T: Copy + Ord>(list: &[T]) -> T { let mut largest = list[0]; for &item in list.iter() { if item > largest { largest = item; } } largest } ``` [[メソッド (Rust)|メソッド]]定義の場合、`impl`に`<T>`が必要。 ```rust impl<T> Point<T> { fn x(&self) -> &T { &self.x } } ``` 特定の[[ジェネリックな型]]をもつ場合のみ[[メソッド (Rust)|メソッド]]が実装されるケース。 ```rust impl Point<f32> { fn x(&self) -> &f32 { &self.x } } ``` - [[ジェネリクス (Rust)|ジェネリクス]]を使ってもコードの実行は遅くならない - [[単相化]]のおかげ たとえば以下のコードがあるとき ```rust let integer = Some(5); let float = Some(5.0); ``` [[単相化]]されると以下のようになる。 ```rust // _i32 で具体化 enum Option_i32 { Some(i32), None, } // _f64 で具体化 enum Option_f64 { Some(f64), None, } fn main() { let integer = Option_i32::Some(5); let float = Option_f64::Some(5.0); } ``` ## トレイト: 共通の振る舞いを定義する `main.rs` ```rust // Summaryをuseしないとtweet.summarize()がエラーになる use trait_sandbox::{Summary, Tweet}; fn main() { let tweet = Tweet { username: String::from("hourse_ebooks"), content: String::from("of course, as you probably already know, people"), reply: false, retweet: false }; println!("1 new tweet: {}", tweet.summarize()); } ``` `lib.rs` ```rust pub trait Summary { fn summarize(&self) -> String; } pub struct NewsArticle { pub headline: String, pub location: String, pub author: String, pub content: String, } impl Summary for NewsArticle { fn summarize(&self) -> String { format!("{}, by {} ({})", self.headline, self.author, self.location) } } pub struct Tweet { pub username: String, pub content: String, pub reply: bool, pub retweet: bool, } impl Summary for Tweet { fn summarize(&self) -> String { format!("{}: {}", self.username, self.content) } } ``` - [[Rustの外部トレイトは外部の型には実装できない]] ([[コヒーレンス]]) - [[Rustの外部トレイトは内部の型に実装できる]] - [[Rustの内部トレイトは外部の型に実装できる]] - [[デフォルト実装]] - [[impl Trait構文]] - [[トレイト境界]] - [[複数のトレイト境界を指定]] - [[where句を使ったトレイト境界の指定方法]] - [[Rustのimpl Trait構文でトレイトを実装している型を返す]] > largestの別の実装方法は、関数がスライスのT値への参照を返すようにすることです。 戻り値の型をTではなく&Tに変え、それにより関数の本体を参照を返すように変更したら、 CloneやCopyトレイト境界は必要なくなり、ヒープ確保も避けられるでしょう。 これらの代替策をご自身で実装してみましょう! できた。 ```rust extern crate core; fn largest<T: PartialOrd>(list: &[T]) -> &T { let mut largest = &list[0]; for item in list.iter() { if item > largest { largest = &item; } } largest } fn main() { let xs = vec![1, 10, 5]; let r = largest(&xs); eprintln!("r = {:?}", r); } ``` - [[PartialOrdトレイトとOrdトレイトの違い]] - [[ブランケット実装]] ## ライフタイムで参照を検証する - [[ライフタイム]]の主な目的は[[ダングリング参照]]を回避すること ```rust fn main() { let r; { let x = 5; r = &x; } // rに束縛されたxの参照、xは内側のスコープで定義されているのでここでは無効に... println!("r: {}", r); } ``` [[ライフタイム]]を考慮した書き方。if文の分岐は実行時になるため[[コンパイラ]]は[[ライフタイム]]を推測できないが、参照`x`と`y`、および戻り値がすべて同じ[[ライフタイム]]であると保証できていれば、その長さにかかわらずこの関数は成立すると言える。 ```rust fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } } ``` `string1` > `string2` = `result` という[[ライフタイム]]の長さ関係があるとき、`longest`関数は問題なく動く。 ```rust fn main() { let string1 = String::from("long string is long"); { let string2 = String::from("xyz"); let result = longest(string1.as_str(), string2.as_str()); println!("The longest string is {}", result); } } ``` なぜなら、`result`より長く生きる[[参照]]は`longest`の引数に存在せず、`longest`の戻り値は引数の[[ライフタイム]]と同等になるため。(実際には`string1`と`string2`のどちらと判断されるのかは気になる...) しかし、以下の場合は異なる。`string2`が`longest`の戻り値として返されたとき、`result`が使われるときには`string2`の[[ライフタイム]]は終了しているはず。つまり、`result`は[[ダングリング参照]]になってしまう。 ```rust fn main() { let string1 = String::from("long string is long"); let result; { let string2 = String::from("xyz"); result = longest(string1.as_str(), string2.as_str()); } println!("The longest string is {}", result); } ``` 言い換えると、 ```rust fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { ``` というのは - `x`と`y`の**より短いスコープが`'a`のライフタイム**となる - `x`と`y`と戻り値のスコープの長さが同じである必要はない を意味する。 - [[関数から参照を返す場合、戻り値型のライフタイム引数は、引数のうちどれかのライフタイム引数と一致する必要がある]] - [[構造体のフィールドが参照型の場合は必ずライフタイム注釈が必要]] - [[ライフタイム省略]] - [[ライフタイム省略規則]] [[ライフタイム]]計算の3規則 1. [[参照]]の各引数は、それぞれ独自の[[ライフタイム引数]]を得る 2. [[入力ライフタイム]]引数が1つなら... - [[出力ライフタイム]]引数に同じ[[ライフタイム引数]]を割りあてる 3. [[入力ライフタイム]]引数が2つ以上なら... - そのうちの1つが `&self` や `&mut self`、すなわち[[メソッド (Rust)|メソッド]]だったら - `self`の[[ライフタイム]]を[[出力ライフタイム]]引数に割り当てる - そうでなかったら - 計算は不可能なので、**[[ライフタイム注釈]]を明示する必要あり**