引数がひとつであり、その参照を返すとき、引数と戻り値のライフタイム注釈は省略できる。
成果物
参考
ライフタイム省略
以下の関数はコンパイルできる。参照を返すにも関わらずライフタイム注釈していないのに。
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[..] }
コンパイルできた理由は、ライフタイム省略規則でそのように定められているから。Rust1.0以前は以下のように書かねばならなかった。
fn first_word<'a>(s: &'a str) -> &'a str {
ライフタイム省略規則
- 参照である各引数は、独自のライフタイム引数を得る
- 1つだけ入力ライフタイム引数があるなら、そのライフタイムが全ての出力ライフタイム引数に代入される
- 複数の入力ライフタイム引数があるが、メソッド引数のうちの一つが
&self
や&mut self
なら、self
のライフタイムが全出力ライフタイム引数に代入される
これら3つのルールをすべて適用することでライフタイム省略する。この規則だけではライフタイムが判明しないとき、ライフタイム注釈にて明示する。さもなくばコンパイルエラー。
規則 | befor | after |
---|---|---|
1 | fn foo(x: &i32, y: &i32) |
fn foo<'a, 'b>(x: &'a i32, y: &'b i32) |
2 | fn foo(x: &i32) -> &i32 |
fn foo<'a>(x: &'a i32) -> &'a i32 |
3 | impl S { fn foo(&self, p1: &i32) -> &i32 {} } |
impl<'a> S<'a> { fn foo<'a>(&'a self, p1: &'b i32) -> &'a i32 {} } |
省略シーケンスA
以下の関数を定義したとき、3つの省略規則を順にあてはめていく。
fn first_word(s: &str) -> &str {
1. 参照である各引数は、独自のライフタイム引数を得る
fn first_word(s: &'a str) -> &str {
2. 1つだけ入力ライフタイム引数があるなら、そのライフタイムが全ての出力ライフタイム引数に代入される
fn first_word(s: &'a str) -> &'a str {
3. 複数の入力ライフタイム引数があるが、メソッド引数のうちの一つが
&self
や&mut self
なら、self
のライフタイムが全出力ライフタイム引数に代入される
これは適用されない。メソッドではなく、引数に&self
, &mut self
がないから。
結果、戻り値のライフタイムは引数と同じであることが自動的に示された。これにてコンパイルエラーになることもなく、ライフタイムを省略できた。
省略ケースB
おなじく以下の関数を定義したとき、3つの省略規則を順にあてはめていく。
fn longest(x: &str, y: &str) -> &str {
1. 参照である各引数は、独自のライフタイム引数を得る
fn longest<'a, 'b>(x: &'a str, y: &'b str) -> &str {
'a
, 'b
というそれぞれ独自のライフタイムを得る。
2. 1つだけ入力ライフタイム引数があるなら、そのライフタイムが全ての出力ライフタイム引数に代入される
適用されない。ライフタイムを持った引数が1つだけではなく、2つあるため対象外。
3. 複数の入力ライフタイム引数があるが、メソッド引数のうちの一つが
&self
や&mut self
なら、self
のライフタイムが全出力ライフタイム引数に代入される
適用されない。メソッドではなく、引数に&self
, &mut self
がないから。
戻り値のライフタイムが不明のままである。よってコンパイルエラーとなる。ライフタイム省略規則だけでは判明しないため、ライフタイム注釈による明示が必要。
省略ケースC
おなじく以下のimpl
を定義したとき、3つの省略規則を順にあてはめていく。
impl<'a> ImportantExcerpt<'a> { fn level(&self) -> i32 { 3 } }
メソッド実装時はこのようにライフタイム注釈する。
1. 参照である各引数は、独自のライフタイム引数を得る
impl<'a> ImportantExcerpt<'a> { fn level(&'a self) -> i32 { 3 } }
2, 3番目は適用されない。
そもそも戻り値は参照ではないため、ライフタイムについて考慮する必要がない。戻り値の所有権は、戻り値を受け取る側にムーブされる。戻り値のライフタイムは受け取った側のスコープとなる。
省略ケースD
impl<'a> ImportantExcerpt<'a> { fn announce_and_return_part(&self, announcement: &str) -> &str { println!("Attention please: {}", announcement); self.part } }
1. 参照である各引数は、独自のライフタイム引数を得る
impl<'a> ImportantExcerpt<'a> { fn announce_and_return_part(&'a self, announcement: &'b str) -> &str { println!("Attention please: {}", announcement); self.part } }
第一引数に'a
, 第二引数に'b
とそれぞれ独自のライフタイムを得る。
2. 1つだけ入力ライフタイム引数があるなら、そのライフタイムが全ての出力ライフタイム引数に代入される
適用されない。ライフタイムを持った引数が1つだけではなく、2つあるため対象外。
3. 複数の入力ライフタイム引数があるが、メソッド引数のうちの一つが
&self
や&mut self
なら、self
のライフタイムが全出力ライフタイム引数に代入される
impl<'a> ImportantExcerpt<'a> { fn announce_and_return_part(&'a self, announcement: &'b str) -> &'a str { println!("Attention please: {}", announcement); self.part } }
戻り値のライフタイムは&self
とおなじ'a
となる。
戻り値のライフタイムが判明したので、コンパイルエラーにならず成功する。
対象環境
- Raspbierry pi 3 Model B+
- Raspbian stretch 9.0 2018-11-13
- bash 4.4.12(1)-release
- rustc 1.34.2 (6c2484dc3 2019-05-13)
- cargo 1.34.0 (6789d8a0a 2019-04-01)
$ uname -a Linux raspberrypi 4.19.42-v7+ #1219 SMP Tue May 14 21:20:58 BST 2019 armv7l GNU/Linux
前回まで
- Rustを学んでみたい(プログラミング言語)
- Rustの環境構築
- RustでHelloWorld
- Rustの和訳ドキュメント
- Cargoでプロジェクト作成・ビルド・実行
- クレートとは?
- Rustで関数を使ってみる
- Rustでモジュールを使ってみる
- Rustで乱数を生成する(rand)
- Rustで標準入力する(std::io::stdin().read_line())
- RustでMatch判定する(match)
- Rustでprintとread_lineを1行にする方法
- Rustで数当てゲーム
- クレート名にドット.が使えない
- Rustの変数と可変性(let, mut) error[E0384]: cannot assign twice to immutable variable
x
- Rustのimmutable束縛とconst定数の違い
- RustのREPL、evcxrのインストールに失敗した
- Rustでコンパイルするときの変数未使用warningを消す
- Rustの変数(再代入、再宣言(シャドーイング))
- Rustのシャドーイングについて
- イミュータブルについて(副作用)
- Rustの定数(const)
- Rustのデータ型(数値)
- Rustのデータ型(論理)
- Rustのデータ型(文字)
- Rustのデータ型(タプル)
- Rustのデータ型(配列)
- Rustの関数
- Rustのif式
- Rustのくりかえし文(loop)
- Rustのくりかえし文(while)
- Rustのくりかえし文(for)
- Rustの所有権(ムーブ)
- Rustの所有権(関数)
- Rustの所有権(スライス)
- Rustの構造体(定義とインスタンス化)
- Rustの構造体(プログラム例)
- Rustの構造体(メソッド)
- Rustの列挙型(enum)
- Rustの列挙型(enum)
- Rustの列挙型(enum)
- Rustのmatch(制御フロー演算子)
- RustでNULLを扱う(Option, Some, None)
- NULL参照は10億ドルの失敗だった
- Rustの列挙型に独自表示を実装する(E0277 対策 std::fmt::Display 実装)
- RustのIfLet(matchの糖衣構文)
- Rustのプロジェクト構造
- Rustのcargoでライブラリ&テスト(単体、結合)
- Rustのモジュール(mod)
- Rustのモジュール(pub)
- Rustのmod参照方法(
mod 子モジュール名;
,use 要素名;
,extern crate クレート名;
,super
) - Rustのインポートまとめ(Rust2018)
- RustのコレクションVec型
- RustのコレクションString型
- RustのコレクションHashMap型
- Rustのコレクション(練習問題)
- Rustのエラー処理
- Rustのジェネリクス
- Rustのトレイト
- Rustのライフタイム1
- Rustのライフタイム2(構造体の定義)