<style> /* basic design */ .reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6, .reveal section, .reveal table, .reveal li, .reveal blockquote, .reveal th, .reveal td, .reveal p { font-family: 'Meiryo UI', 'Source Sans Pro', Helvetica, sans-serif, 'Helvetica Neue', 'Helvetica', 'Arial', 'Hiragino Sans', 'ヒラギノ角ゴシック', YuGothic, 'Yu Gothic'; text-align: left; line-height: 1.6; letter-spacing: normal; text-shadow: none; word-wrap: break-word; color: #444; } .reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6 {font-weight: bold;} .reveal h1, .reveal h2, .reveal h3 {color: #2980b9;} .reveal th {background: #DDD;} .reveal section img {background:none; border:none; box-shadow:none; max-width: 95%; max-height: 95%;} .reveal blockquote {width: 90%; padding: 0.5vw 3.0vw;} .reveal table {margin: 1.0vw auto;} .reveal code {line-height: 1.2;} .reveal p, .reveal li {padding: 0vw; margin: 0vw;} .reveal .box {margin: -0.5vw 1.5vw 2.0vw -1.5vw; padding: 0.5vw 1.5vw 0.5vw 1.5vw; background: #EEE; border-radius: 1.5vw;} /* table design */ .reveal table {background: #f5f5f5;} .reveal th {background: #444; color: #fff;} .reveal td {position: relative; transition: all 300ms;} .reveal tbody:hover td { color: transparent; text-shadow: 0 0 3px #aaa;} .reveal tbody:hover tr:hover td {color: #444; text-shadow: 0 1px 0 #fff;} /* blockquote design */ .reveal blockquote { width: 90%; padding: 0.5vw 0 0.5vw 6.0vw; font-style: italic; background: #f5f5f5; } .reveal blockquote:before{ position: absolute; top: 0.1vw; left: 1vw; content: "\f10d"; font-family: FontAwesome; color: #2980b9; font-size: 3.0vw; } /* font size */ .reveal h1 {font-size: 5.0vw;} .reveal h2 {font-size: 4.0vw;} .reveal h3 {font-size: 2.8vw;} .reveal h4 {font-size: 2.6vw;} .reveal h5 {font-size: 2.4vw;} .reveal h6 {font-size: 2.2vw;} .reveal section, .reveal table, .reveal li, .reveal blockquote, .reveal th, .reveal td, .reveal p {font-size: 2.2vw;} .reveal code {font-size: 1.6vw;} /* new color */ .red {color: #EE6557;} .blue {color: #16A6B6;} /* split slide */ #right {left: -18.33%; text-align: left; float: left; width: 50%; z-index: -10;} #left {left: 31.25%; text-align: left; float: left; width: 50%; z-index: -10;} </style> <style> /* specific design */ .reveal h2 { padding: 0 1.5vw; margin: 0.0vw 0 2.0vw -2.0vw; border-left: solid 1.2vw #2980b9; border-bottom: solid 0.8vw #d7d7d7; } </style> <!-- --------------------------------------------------------------------------------------- --> # Rust勉強会 第9回 ## 10章 ジェネリック型、トレイト、ライフタイム ### 2020/10/02 原山和之 --- ## 本日のメニュー 1. ジェネリック型 1. トレイト 1. ライフタイム --- ## はじめに ジェネリクスとは? ジェネリクスは、具体型や他のプロパティの抽象的な代役です。 コード記述の際、コンパイルやコード実行時に、 ジェネリクスの位置に何が入るかを知ることなく、ジェネリクスの振る舞いや他のジェネリクスとの関係を表現できるのです。 概念の重複を効率的に扱う道具 --- ## 復習 ジェネリック型は以前の章に出てきました。 第4章で参照、借用 第6章で Option\<T\> 第8章でVec\<T\>とHashMap\<K, V\> 第9章でResult\<T, E\> --- ## 関数重複を取り除く1 最大値を求めるプログラム例: ```rust fn main() { let number_list = vec![34, 50, 25, 100, 65]; let mut largest = number_list[0]; for number in number_list { if number > largest { largest = number; } } // 最大値は{}です println!("The largest number is {}", largest); } ``` --- ## 関数重複を取り除く2 重複がある例: ```rust fn main() { let number_list = vec![34, 50, 25, 100, 65]; let mut largest = number_list[0]; for number in number_list { if number > largest { largest = number; } } println!("The largest number is {}", largest); let number_list = vec![102, 34, 6000, 89, 54, 2, 43, 8]; let mut largest = number_list[0]; for number in number_list { if number > largest { largest = number; } } println!("The largest number is {}", largest); } ``` --- ## 関数重複を取り除く3 重複を修正した例: ```rust fn largest(list: &[i32]) -> i32 { let mut largest = list[0]; for &item in list.iter() { if item > largest { largest = item; } } largest } fn main() { let number_list = vec![34, 50, 25, 100, 65]; let result = largest(&number_list); println!("The largest number is {}", result); let number_list = vec![102, 34, 6000, 89, 54, 2, 43, 8]; let result = largest(&number_list); println!("The largest number is {}", result); } ``` --- ## 重複する2つの関数 例: 1つはi32値のスライスから最大の要素を探す関数 1つはchar値のスライスから最大要素を探す関数 ```rust fn largest_i32(list: &[i32]) -> i32 { let mut largest = list[0]; for &item in list.iter() { if item > largest { largest = item; } } largest } fn largest_char(list: &[char]) -> char { let mut largest = list[0]; for &item in list.iter() { if item > largest { largest = item; } } largest } fn main() { let number_list = vec![34, 50, 25, 100, 65]; let result = largest_i32(&number_list); println!("The largest number is {}", result); let char_list = vec!['y', 'm', 'a', 'q']; let result = largest_char(&char_list); println!("The largest char is {}", result); } ``` --- ## 関数の引数にジェネリクスなデータ型 "type"の省略形なので、Tが多くのRustプログラマの既定の選択 コンパイルエラーが出ます!! ```rust fn largest<T>(list: &[T]) -> T { let mut largest = list[0]; for &item in list.iter() { if item > largest { largest = item; } } largest } fn main() { let number_list = vec![34, 50, 25, 100, 65]; let result = largest(&number_list); println!("The largest number is {}", result); let char_list = vec!['y', 'm', 'a', 'q']; let result = largest(&char_list); println!("The largest char is {}", result); } ``` --- ## 今日のエラー ``` error[E0369]: binary operation `>` cannot be applied to type `T` (エラー: 2項演算`>`は、型`T`に適用できません) --> src/main.rs:5:12 | 5 | if item > largest { | ^^^^^^^^^^^^^^ | = note: an implementation of `std::cmp::PartialOrd` might be missing for `T` (注釈: `std::cmp::PartialOrd`の実装が`T`に対して存在しない可能性があります) ``` 注釈がstd::cmp::PartialOrdに触れています。これは、トレイトです。 とりあえず、このエラーは、largestの本体は、Tがなりうる全ての可能性のある型に対して動作しないと述べています。 本体で型Tの値を比較したいので、値が順序付け可能な型のみしか使用できないのです。比較を可能にするために、 標準ライブラリには型に実装できるstd::cmp::PartialOrdトレイトがあります(このトレイトについて詳しくは付録Cを参照されたし)。 ジェネリックな型が特定のトレイトを持つと指定する方法は「トレイト境界」節で習うでしょうが、 先にジェネリックな型引数を使用する他の方法を探究しましょう --- ## 構造体定義でのジェネリクス1 ```rust #[derive(Debug)] // 第5章でやったやつ struct Point<T> { x: T, y: T, } fn main() { let integer = Point { x: 5, y: 10 }; let float = Point { x: 1.0, y: 4.0 }; // デバッグしたかったら println!("Debug {:?}", integer); println!("Debug {:?}", float); } ``` --- ## 構造体定義でのジェネリクス2 エラー どちらも同じジェネリックなデータ型Tなので、xとyというフィールドは同じ型でなければならない ```rust #[derive(Debug)] // 第5章でやったやつ struct Point<T> { x: T, y: T, } fn main() { let point = Point { x: 5, y: 4.0 }; // デバッグしたかったら println!("Debug {:?}", point); } ``` --- ## 構造体定義でのジェネリクス3 エラーを修正すると ```rust #[derive(Debug)] struct Point<T, U> { x: T, y: U, } fn main() { let point = Point { x: 5, y: 4.0 }; // デバッグしたかったら println!("Debug {:?}", point); } ``` --- ## Enum定義でのジェネリクス1 6章で出てきたOption\<T\> この意味は... Option\<T\>は、 型Tに関してジェネリックで2つの列挙子のあるenumです その列挙子は、型Tの値を保持するSomeと、 値を何も保持しないNoneです。 ```rust enum Option<T> { Some(T), None, } ``` --- ## Enum定義でのジェネリクス2 9章のenum Result<T, E> TとEは、ジェネリックな型引数です。 Tが成功した時にOk列挙子に含まれて返される値の型を表すことと、 Eが失敗した時にErr列挙子に含まれて返されるエラーの型を表すことです。 ```rust enum Result<T, E> { Ok(T), Err(E), } ``` --- ## メソッド定義でのジェネリクス1 定義にジェネリックな型を使うメソッドを構造体やenumに実装することもできます。 implの直後にTを宣言しなければならないことに注意してください。コンパイラに認識させるためです。 ```rust struct Point<T> { x: T, y: T, } impl<T> Point<T> { fn x(&self) -> &T { &self.x } } fn main() { let p = Point { x: 5, y: 10 }; println!("p.x = {}", p.x()); } ``` --- ## メソッド定義でのジェネリクス2 ジェネリックな型を持つPoint\<T>インスタンスではなく、Point\<f32>だけにメソッドを実装することもできる ```rust impl Point<f32> { fn distance_from_origin(&self) -> f32 { (self.x.powi(2) + self.y.powi(2)).sqrt() } } ``` --- ## メソッド定義でのジェネリクス2 異なった型を返すこともできる x(値は5)にi32、y(値は10.4)にf64を持つPointを定義しました。p2変数は、 x(値は"Hello")に文字列スライス、y(値はc)にcharを持つPoint構造体です。 ```rust struct Point<T, U> { x: T, y: U, } impl<T, U> Point<T, U> { fn mixup<V, W>(self, other: Point<V, W>) -> Point<T, W> { Point { x: self.x, y: other.y, } } } fn main() { let p1 = Point { x: 5, y: 10.4 }; let p2 = Point { x: "Hello", y: 'c'}; let p3 = p1.mixup(p2); println!("p3.x = {}, p3.y = {}", p3.x, p3.y); } ``` --- ## ジェネリクスを使用したコードのパフォーマンス Rustでは、ジェネリクスを、具体的な型があるコードよりもジェネリックな型を使用したコードを実行するのが遅くならないように実装しています。 コンパイラはこれを、ジェネリクスを使用しているコードの単相化をコンパイル時に行うことで達成しています。 単相化(monomorphization)は、コンパイル時に使用されている具体的な型を入れることで、 ジェネリックなコードを特定のコードに変換する過程のことです。 --- ## 単相化 ```rust enum Option_i32 { Some(i32), None, } enum Option_f64 { Some(f64), None, } fn main() { let integer = Option_i32::Some(5); let float = Option_f64::Some(5.0); } ``` --- ## トレイト * トレイトを使用すると、共通の振る舞いを抽象的に定義 * トレイト境界を使用すると、 あるジェネリックが、特定の振る舞いをもつあらゆる型になり得ることを指定できます。 --- ## トレイトを定義 ```rust pub trait Summary { // トレイト名 fn summarize(&self) -> String; // トレイトシグニチャ } ``` --- ## トレイトを型に実装 NewsArticle または Tweet インスタンスに保存されているデータのサマリーを表示できるメディア アグリゲータ ライブラリを作成します。 各型のサマリーが必要で、インスタンスで summarize メソッドを呼び出してサマリーを要求する ```rust pub struct NewsArticle { pub headline: String, pub location: String, pub author: String, pub content: String, } impl Summary for NewsArticle { // forキーワードを使用 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 { // forキーワードを使用 fn summarize(&self) -> String { // トレイト定義で定義したメソッドシグニチャを置 format!("{}: {}", self.username, self.content) } } ``` --- ## トレイトを型の使い方 トレイトを実装後、普通のメソッド同様にNewsArticleやTweetのインスタンスに対してこのメソッドを呼び出せます。 ```rust let tweet = Tweet { username: String::from("horse_ebooks"), content: String::from( // もちろん、ご存知かもしれませんがね、みなさん "of course, as you probably already know, people", ), reply: false, retweet: false, }; println!("1 new tweet: {}", tweet.summarize()); ``` ``` 1 new tweet: horse_ebooks: of course, as you probably already know, people ``` --- ## トレイトのデフォルト実装定義 メソッドシグニチャだけを定義するのではなく、 Summaryトレイトのsummarizeメソッドにデフォルトの文字列を指定する 定義: ```rust pub trait Summary { fn summarize(&self) -> String { // "(もっと読む)" String::from("(Read more...)") } } ``` --- ## トレイトのデフォルト実装の使い方 ```rust let article = NewsArticle { // ペンギンチームがスタンレーカップチャンピオンシップを勝ち取る! headline: String::from("Penguins win the Stanley Cup Championship!"), // アメリカ、ペンシルベニア州、ピッツバーグ location: String::from("Pittsburgh, PA, USA"), // アイスバーグ author: String::from("Iceburgh"), // ピッツバーグ・ペンギンが再度NHL(National Hockey League)で最強のホッケーチームになった content: String::from( "The Pittsburgh Penguins once again are the best \ hockey team in the NHL.", ), }; println!("New article available! {}", article.summarize()); ``` ``` New article available! (Read more...) ``` --- ## トレイトのデフォルト実装で変更を最小限に 定義: ```rust pub trait Summary { fn summarize_author(&self) -> String; fn summarize(&self) -> String { // "({}さんの文章をもっと読む)" format!("(Read more from {}...)", self.summarize_author()) } } ``` --- ## トレイトのデフォルト実装で変更を最小限に オーバーライド ```rust impl Summary for Tweet { fn summarize_author(&self) -> String { format!("@{}", self.username) } } ``` --- ## トレイトのデフォルト実装で変更を最小限3 使い方 ```rust let tweet = Tweet { username: String::from("horse_ebooks"), content: String::from( "of course, as you probably already know, people", ), reply: false, retweet: false, }; println!("1 new tweet: {}", tweet.summarize()); ``` ``` 1 new tweet: (Read more from @horse_ebooks...) ``` --- ## 引数としてのトレイト この関数を呼び出すときに、Stringやi32のような他の型を渡すようなコードはコンパイルできません。 この引数は、指定されたトレイトを実装しているあらゆる型を受け付けます。 トレイト境界 (trait bound) と呼ばれる姿の糖衣構文 (syntax sugar) ```rust pub fn notify(item: &impl Summary) { println!("Breaking news! {}", item.summarize()); } ``` --- ## トレイト境界構文1 もともとは下記のように書きます。 ```rust pub fn notify<T: Summary>(item: &T) { // 速報! {} println!("Breaking news! {}", item.summarize()); } ``` --- ## トレイト境界構文2 2つの引数を取る表現 ```rust pub fn notify(item1: &impl Summary, item2: &impl Summary) { ``` ```rust pub fn notify<T: Summary>(item1: &T, item2: &T) { ``` --- ## 複数のトレイト境界を+構文 summarizeメソッドに加えてitemの画面出力形式(ディスプレイフォーマット)を使わせたいとします。 notifyの定義にitemはDisplayとSummaryの両方を実装していなくてはならないと指定することってのができます。 ```rust pub fn notify(item: &(impl Summary + Display)) { ``` ```rust pub fn notify<T: Summary + Display>(item: &T) { ``` --- ## where句を使ったより明確なトレイト境界 関数名と引数のリストが大量のトレイト境界に関する情報で非常に見にくくなります。 ```rust fn some_function<T: Display + Clone, U: Clone + Debug>(t: &T, u: &U) -> i32 { ``` Where句を使うことができます。 ```rust fn some_function<T, U>(t: &T, u: &U) -> i32 where T: Display + Clone, U: Clone + Debug { ``` --- ## トレイトを実装している型を返す impl Summaryを使うことにより、 具体的な型が何かを言うことなく、returns_summarizable関数はSummaryトレイトを実装している何らかの型を返す impl Traitは一種類の型を返す場合にのみ使える。 ```rust fn returns_summarizable() -> impl Summary { Tweet { username: String::from("horse_ebooks"), content: String::from( "of course, as you probably already know, people", ), reply: false, retweet: false, } } ``` --- ## トレイト境界でlargest関数を修正する1 今日のエラー ```$ cargo run Compiling chapter10 v0.1.0 (file:///projects/chapter10) error[E0369]: binary operation `>` cannot be applied to type `T` --> src/main.rs:5:17 | 5 | if item > largest { | ---- ^ ------- T | | | T | = note: `T` might need a bound for `std::cmp::PartialOrd` error: aborting due to previous error For more information about this error, try `rustc --explain E0369`. error: could not compile `chapter10`. To learn more, run the command again with --verbose. ``` 下記に書き換えてみる ```rust fn largest<T: PartialOrd>(list: &[T]) -> T { ``` --- ## エラーの解消方法 * 引数のlistは、i32やcharのようなサイズが既知の型は スタックに格納できるので、Copyトレイトを実装しているが、 list引数がCopyトレイトを実装しない型を含む可能性があるためにlist[0]から値をlargestにムーブできず ```rust fn largest<T: PartialOrd + Copy>(list: &[T]) -> T { ``` --- ## トレイト境界を使用して、メソッド実装を条件分けする ジェネリックな型引数を持つimplブロックにトレイト境界を与えることで、 特定のトレイトを実装する型に対するメソッド実装を条件分けできます。例えば、 型Pair\<T\>は、常にnew関数を実装します。しかし、Pair\<T\>は、 内部の型Tが比較を可能にするPartialOrdトレイトと出力を可能にするDisplayトレイトを実装している時のみ、 cmp_displayメソッドを実装します。 ```rust use std::fmt::Display; struct Pair<T> { x: T, y: T, } impl<T> Pair<T> { fn new(x: T, y: T) -> Self { Self { x, y } } } impl<T: Display + PartialOrd> Pair<T> { fn cmp_display(&self) { if self.x >= self.y { println!("The largest member is x = {}", self.x); } else { println!("The largest member is y = {}", self.y); } } } ``` --- ## ライフタイム 参照を検証する * Rustにおいて参照は全てライフタイムを保持するということ * ライフタイムとは、その参照が有効になるスコープのこと * 多くの場合、型が推論されるように、 大体の場合、ライフタイムも暗黙的に推論される * 詳しくは19章で --- ## ライフタイムでダングリング参照を回避する * 宙に浮いた参照の回避方法。 * Rustではnull値は許可されないはずです。 * 下記はエラーになる。 ```rust { let r; { let x = 5; r = &x; } println!("r: {}", r); } ``` --- ## 借用チェッカー コンパイラには下記のように見えている。 ```rust { let r; // ---------+-- 'a // | { // | let x = 5; // -+-- 'b | r = &x; // | | } // -+ | // | println!("r: {}", r); // | } // ---------+ ``` --- ## ダングリングの修正 ```rust { let x = 5; // ----------+-- 'b // | let r = &x; // --+-- 'a | // | | println!("r: {}", r); // | | // --+ | } // ----------+ ``` --- ## 関数のジェネリックなライフタイム 関数に取ってほしい引数が文字列スライス、つまり参照であることに注意してください。 何故なら、longest関数に引数の所有権を奪ってほしくない スライスと文字列リテラルを受け取らせて長さの比較を行うプログラムです。 コンパイルできない例 ```rust fn longest(x: &str, y: &str) -> &str { if x.len() > y.len() { x } else { y } } fn main() { let string1 = String::from("abcd"); let string2 = "xyz"; let result = longest(string1.as_str(), string2); } ``` --- ## エラーの内容 返している参照がxかyのどちらを参照しているか、コンパイラにはわからない ``` error[E0106]: missing lifetime specifier (エラー: ライフタイム指定子が不足しています) --> src/main.rs:1:33 | 1 | fn longest(x: &str, y: &str) -> &str { | ^ expected lifetime parameter | (ライフタイム引数が予想されます) | = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `x` or `y` (助言: この関数の戻り値型は借用された値を含んでいますが、 シグニチャは、それが`x`か`y`由来のものなのか宣言していません) ``` --- ## ライフタイム注釈記法 ```rust &i32 // a reference // (ただの)参照 &'a i32 // a reference with an explicit lifetime // 明示的なライフタイム付きの参照 &'a mut i32 // a mutable reference with an explicit lifetime // 明示的なライフタイム付きの可変参照 ``` --- ## ライフタイム付きに変更 参照間の関係を定義するジェネリックなライフタイム引数 シグニチャの全参照が同じライフタイム'aになると指定したlongest関数の定義 このシグニチャで表現したい制約は、引数の全参照と戻り値が同じライフタイムになること ```rust fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } } ``` --- ## ライフタイム注釈がことなる参照を渡す string1は外側のスコープの終わりまで有効で、string2は内側のスコープの終わりまで有効、 そしてresultは内側のスコープの終わりまで有効な何かを参照 ```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の参照のライフタイムが2つの引数の小さい方のライフタイムになることを示す例 resultがprintln!文に対して有効 無効な参照がある可能性があるとして許可しない ```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); } ``` --- ## ライフタイムの観点で思考実験1 ワーニングは出ますが、これはコンパイルできます。 ```rust fn longest<'a>(x: &'a str, y: &str) -> &'a str { x } ``` --- ## ライフタイムの観点で思考実験2 これはコンパイルできません。ダングリングですね。 ```rust fn longest<'a>(x: &str, y: &str) -> &'a str { // 本当に長い文字列 let result = String::from("really long string"); result.as_str() // ここでスコープが有効でなくなります。 } ``` --- ## 構造体定義のライフタイム注釈 構造体に参照を保持させることもできます 例: 参照を含む構造体なので、定義にライフタイム注釈が必要 文字列スライスを保持するImportantExcerpt(重要な一節)という構造 ```rust struct ImportantExcerpt<'a> { part: &'a str, } fn main() { // 僕をイシュマエルとお呼び。何年か前・・・ let novel = String::from("Call me Ishmael. Some years ago..."); let first_sentence = novel.split('.') .next() .expect("Could not find a '.'"); // '.'が見つかりませんでした let i = ImportantExcerpt { part: first_sentence }; println!("{}", i.part) } ``` --- ## ライフタイムの省略 ライフタイム注釈なしでコンパイルできる ```rust fn first_word(s: &str) -> &str { let bytes = s.as_bytes(); for (i, &item) in bytes.iter().enumerate() { if item == b' ' { return &s[0..i]; } } &s[..] } ``` 歴史的経緯らしいです。。。このパターンをコンパイラのコードに落とし込んだので、 このような場面には借用チェッカーがライフタイムを推論できるらしい。。。なんと。 ```rust fn first_word<'a>(s: &'a str) -> &'a str { ``` --- ## ライフタイム規則 * 1引数の関数は、1つのライフタイム引数を得るということです: fn foo<'a>(x: &'a i32); 2つ引数のある関数は、2つの個別のライフタイム引数を得ます: fn foo<'a, 'b>(x: &'a i32, y: &'b i32); * 2番目の規則は、1つだけ入力ライフタイム引数があるなら、そのライフタイムが全ての出力ライフタイム引数に代入されるというものです: fn foo<'a>(x: &'a i32) -> &'a i32。 * 複数の入力ライフタイム引数があるけれども、メソッドなのでそのうちの一つが&selfや&mut selfだったら、 selfのライフタイムが全出力ライフタイム引数に代入されるというものです。 --- ## 構造体のメソッド定義におけるライフタイム注釈 ```rust impl<'a> ImportantExcerpt<'a> { fn level(&self) -> i32 { 3 } } ``` ```rust impl<'a> ImportantExcerpt<'a> { fn announce_and_return_part(&self, announcement: &str) -> &str { // お知らせします println!("Attention please: {}", announcement); self.part } } ``` --- ## 静的ライフタイム 議論する必要のある1種の特殊なライフタイムが、'staticであり、これはプログラム全体の期間を示します。 文字列リテラルは全て'staticライフタイムになり、次のように注釈できます: ```rust // 静的ライフタイムを持ってるよ let s: &'static str = "I have a static lifetime."; ``` --- ## まとめ ```rust use std::fmt::Display; fn longest_with_an_announcement<'a, T>(x: &'a str, y: &'a str, ann: T) -> &'a str where T: Display { // アナウンス! println!("Announcement! {}", ann); if x.len() > y.len() { x } else { y } } ``` --- ## 参考資料 --- # ご清聴ありがとうございました
{"metaMigratedAt":"2023-06-15T13:33:19.190Z","metaMigratedFrom":"YAML","title":"第9回 10章ジェネリック型、トレイト、ライフタイム","breaks":true,"description":"Rust勉強会第9回のスライド","slideOptions":"{\"theme\":\"white\",\"slideNumber\":\"c/t\",\"center\":false,\"transition\":\"none\",\"keyboard\":true}","contributors":"[{\"id\":\"ed5d0581-544f-4aa0-a6ad-2f48be3d325d\",\"add\":24014,\"del\":2764}]"}
    1083 views