3つの機能について。
成果物
参照
3つの機能
- ライフタイム・サブタイピング: あるライフタイムが他のライフタイムより長生きすることを保証する
- ライフタイム境界: ジェネリックな型への参照のライフタイムを指定する
- トレイトオブジェクトのライフタイムの推論: コンパイラにトレイトオブジェクトのライフタイムを推論させることと指定する必要があるタイミング
ライフタイム・サブタイピング
ライフタイム・サブタイピングは、あるライフタイムが他のライフタイムより長生きすべきであることを指定する。
struct Context(&str); struct Parser { context: &Context, } impl Parser { fn parse(&self) -> Result<(), &str> { Err(&self.context.0[1..]) } } fn main() { println!("Hello Rust !!"); }
$ rustc main.rs error[E0106]: missing lifetime specifier --> main.rs:5:16 | 5 | struct Context(&str); | ^ expected lifetime parameter error[E0106]: missing lifetime specifier --> main.rs:7:14 | 7 | context: &Context, | ^ expected lifetime parameter
参照にライフタイム指定子を与えてコンパイル可にしたコードは以下。
struct Context<'a>(&'a str); struct Parser<'a> { context: &'a Context<'a>, } impl<'a> Parser<'a> { fn parse(&self) -> Result<(), &str> { Err(&self.context.0[1..]) } }
parse_context
関数を追加すると以下エラー。
fn parse_context(context: Context) -> Result<(), &str> { Parser { context: &context }.parse() }
$ rustc main.rs error[E0597]: borrowed value does not live long enough --> main.rs:18:5 | 18 | Parser { context: &context }.parse() | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ temporary value does not live long enough 19 | } | - temporary value only lives until here | note: borrowed value must be valid for the anonymous lifetime #1 defined on the function body at 17:1... --> main.rs:17:1 | 17 | / fn parse_context(context: Context) -> Result<(), &str> { 18 | | Parser { context: &context }.parse() 19 | | } | |_^ error[E0597]: `context` does not live long enough --> main.rs:18:24 | 18 | Parser { context: &context }.parse() | ^^^^^^^ borrowed value does not live long enough 19 | } | - borrowed value only lives until here | note: borrowed value must be valid for the anonymous lifetime #1 defined on the function body at 17:1... --> main.rs:17:1 | 17 | / fn parse_context(context: Context) -> Result<(), &str> { 18 | | Parser { context: &context }.parse() 19 | | } | |_^
生成されたParser
とcontext
がparse_context
関数内までしか生存しない。
原因を探る。parse
メソッドについて見てみる。ライフタイム省略規則により、以下の意味となる。
fn parse(&self) -> Result<(), &str> {
fn parse<'a>(&'a self) -> Result<(), &'a str> {
parse
が返すResult
はParse
構造体インスタンスと同じである。
再びparse_context
関数をみてみると、同関数内でParserインスタンスを生成している。つまり関数終了時にライフタイム期間終了となり死ぬ。それを返しているため、上記のコンパイルエラーとなっている。
struct Context<'s>(&'s str); struct Parser<'c, 's> { context: &'c Context<'s>, } impl<'c, 's> Parser<'c, 's> { fn parse(&self) -> Result<(), &'s str> { Err(&self.context.0[1..]) } } fn parse_context(context: Context) -> Result<(), &str> { Parser { context: &context }.parse() }
ドキュメントではerror[E0491]: in type
&'c Context<'s>, reference has a longer lifetime than the data it references
になるとある。だが、私の環境では以下警告が出ただけ。
warning: struct is never constructed: `Context` --> main.rs:8:1 | 8 | struct Context<'s>(&'s str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: #[warn(dead_code)] on by default warning: struct is never constructed: `Parser` --> main.rs:9:1 | 9 | struct Parser<'c, 's> { | ^^^^^^^^^^^^^^^^^^^^^ warning: method is never used: `parse` --> main.rs:13:5 | 13 | fn parse(&self) -> Result<(), &'s str> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: function is never used: `parse_context` --> main.rs:17:1 | 17 | fn parse_context(context: Context) -> Result<(), &str> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ライフタイム's
が少なくとも'c
と同じ期間だけ生きることを指定するコードは以下。
struct Parser<'c, 's: 'c> { context: &'c Context<'s>, }
ライフタイム境界
ライフタイム境界とは、ジェネリックな型へライフタイム引数を追加することである。ジェネリック型内部の参照が参照先データよりも長生きしないことを確かめる。
struct Ref<'a, T>(&'a T);
ドキュメントではerror[E0309]: the parameter type
Tmay not live long enough
になるようだが、私の環境ではエラーにならない。
ドキュメントでは、上記エラーを以下で解決できるとある。
struct Ref<'a, T: 'a>(&'a T);
'static
にもできる。(プログラム終了まで生存)
struct StaticRef<T: 'static>(&'static T);
トレイとオブジェクトのライフタイム推論
trait Red { } struct Ball<'a> { diameter: &'a i32, } impl<'a> Red for Ball<'a> { } fn main() { let num = 5; let obj = Box::new(Ball { diameter: &num }) as Box<Red>; }
objに関連するライフタイムを注釈していないものの、このコードはエラーなくコンパイルできます。 ライフタイムとトレイトオブジェクトと共に働く規則があるので、このコードは動くのです:
- トレイトオブジェクトのデフォルトのライフタイムは、
'static
&'a Trait
や&'a mut Trait
に関して、トレイトオブジェクトのデフォルトのライフタイムは'a
- 単独のT:
'a
節について、トレイトオブジェクトのデフォルトのライフタイムは、'a
- 複数のT:
'a
のような節について、デフォルトのライフタイムはない; 明示しなければならない
明示せねばならないとき、以下の記法にてライフタイム境界を追加できる。
Box<Red + 'static>
Box<Red + 'a>
ドキュメントにはコード例がなかった。どう書けばいいのかわからない。予想すると以下。
let obj: Box<Red + 'a> = Box::new<'a>(Ball<'a> { diameter: &'a num }) as Box<Red + '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(構造体の定義)
- Rustのライフタイム3(ライフタイム省略)
- Rustのライフタイム4(impl定義)
- Rustの静的ライフタイム5('static)
- Rustのライフタイム6(ジェネリクス、トレイト境界とともに)
- Rustのテストコードを書く
- Rustのテスト実行
- Rustのテスト体系化
- Rustでコマンドライン引数を受け取る
- Rustのファイル読込
- Rustでリファクタリング(モジュール性とエラー処理の向上)
- Rustでテスト駆動開発
- Rustで環境変数を取得する
- RustでStdErr出力
- Rustのクロージャ
- Rustのイテレータ
- Rustのイテレータ(Minigrep改善)
- Rustのイテレータ(パフォーマンス)
- Rustのイテレータ(Minigrep改善)
- Rustのcargo(ビルドのカスタマイズ)
- Rustのcargo(cargo docでドキュメント生成)
- Rustのエクスポート(pub use)
- Rustのクレートを公開する方法(crates.io)
- Rustのcargoでワークスペースをつくる
- Rustのcargo installでバイナリをインストールする
- Rustのcargoを拡張する方法
- Rustのスマートポインタ
- スマートポインタBox
- Rustのスマートポインタ(Derefトレイト)
- Rustのスマートポインタ(Dropトレイト)
- Rustのスマートポインタ(Rc
) - Rustのスマートポインタ(RefCell
) - Rustのスマートポインタ(Weak
) - Rustのスレッド
- Rustのスレッド(メッセージ送受信)
- Rustのスレッド(Mutex、Arc)
- Rustのスレッド(Send、Syncトレイト)
- Rustのオブジェクト指向
- RustのOOP(トレイトオブジェクト)
- Rustのオブジェクト指向(デザインパターン)
- Rustのパターン
- Rustのパターン(論駁可能性)
- Rustパターン(記法)
- Rustの高度な機能(Unsafe Rust)