原山和之
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Versions and GitHub Sync Note Insights Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       owned this note    owned this note      
    Published Linked with GitHub
    1
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    --- title: 第9回 10章ジェネリック型、トレイト、ライフタイム tags: Rust, Talk, 勉強会 description: Rust勉強会第9回のスライド slideOptions: theme: white slideNumber: 'c/t' center: false transition: 'none' keyboard: true --- <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 } } ``` --- ## 参考資料 --- # ご清聴ありがとうございました

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully