Rustのエラー E0106: missing lifetime specifier(ライフタイム指定子がない)
エラー E0106 は、関数の戻り値の型や構造体に参照(&)を書いたのに、それが何から借りたものなのか Rust にはわからない、という意味です。あなたのロジックが間違っているわけではありません。コンパイラは、あなたからもうひと言だけ情報がほしいか、ちょっとした方針の変更を求めているだけです。Rust を始めて最初の1週間で、ほとんどの人がこのエラーに出会います。そして、たいていは1行直すだけで解決します。(このページは Rustのコンパイルエラー解説 の一部です。)
エラーメッセージ
Section titled “エラーメッセージ”error[E0106]: missing lifetime specifier --> src/main.rs:1:32 |1 | fn longer(a: &str, b: &str) -> &str { | ---- ---- ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `a` or `b`help: consider introducing a named lifetime parameter |1 | fn longer<'a>(a: &'a str, b: &'a str) -> &'a str { | ++++ ++ ++ ++「ライフタイム指定子がありません。戻り値は借用された値を含んでいますが、それが a と b のどちらから借りたものかがシグネチャに書かれていません」という意味です。help: には、名前付きのライフタイム 'a を加える案が示されています。
このエラーが出るプログラムはこちらです。
fn longer(a: &str, b: &str) -> &str { if a.len() >= b.len() { a } else { b }}
fn main() { println!("{}", longer("apple", "fig"));}どういう意味?
Section titled “どういう意味?”参照は借用です。つまり、ほかの誰かが所有しているデータを指しています。関数が参照を返すときは、それが誰のデータを指しているのかを Rust が知っている必要があります。そうすれば、呼び出し側が結果を使っている間、そのデータがちゃんと生きているかを確かめられるからです。
1行目の下にある3つの印を見てください。コンパイラは a と b に ---- を、戻り値の型に ^ を付けています。「あなたは借用した &str を返していますが、それは a から来たかもしれないし、b から来たかもしれません。どっちですか?」と聞いているのです。Rust は推測で決めたりはしません。
図書館の本にたとえてみましょう。友だちが本を手渡しながら「これ、借りてきたものなんだ」と言ったら、誰から借りたのかを知りたくなりますよね。いつ返さないといけないかがわかるからです。ライフタイムは、借用に付けるこの「貸し主のラベル」のようなものです。
Rust がこうする理由
Section titled “Rust がこうする理由”Rust は、参照が指しているデータより長く生き残ることは絶対にない、と保証しています(いわゆる「ダングリングポインタ」がありません)。関数呼び出しについてこれを確かめるとき、コンパイラが見るのは関数のシグネチャだけで、中身(本体)は見ません。そのため、戻り値の参照がどこから来るのかは、シグネチャ自体に書かれていなければなりません。
とはいえ、ふだんライフタイムを書くことはほとんどありません。Rust が次の3つのシンプルな省略規則(elision rules)で自動的に補ってくれるからです。
- 参照の引数には、それぞれ別のライフタイムが付けられます。
- 参照の引数がちょうど1つなら、戻り値の参照はその引数から借りたものになります。
&selfか&mut selfを持つメソッドなら、戻り値の参照はselfから借りたものになります。
E0106 が出るのは、これらの規則のどれでも答えが決まらないときです。つまり、参照の引数が2つ以上ある(そして self がない)とき、参照の引数が1つもないとき、そして構造体に参照を入れたときです。詳しくはライフタイムのレッスンで、参照そのものについては借用のページで説明しています。
直し方1:名前付きのライフタイム('a)を付ける
Section titled “直し方1:名前付きのライフタイム('a)を付ける”関数が本当に、引数のどれかの一部を返しているときはこれを選びましょう。'a はただの名前です。引数と戻り値の両方に書くと、「結果はこれらの引数と同じだけ生きる」という意味になります。
// 'a:返す &str は a か b から借りるので、両方と同じだけ生きるfn longer<'a>(a: &'a str, b: &'a str) -> &'a str { if a.len() >= b.len() { a } else { b }}
fn main() { let first = String::from("apple"); let second = String::from("fig"); println!("長いほうの単語: {}", longer(&first, &second));}実行結果:
長いほうの単語: apple結果がいつも1つの引数からしか来ないなら、'a はその引数にだけ付けましょう。たとえば fn lookup<'a>(map: &'a HashMap<String, String>, key: &str) -> Option<&'a str> です。コンパイラの提案はすべてに 'a を付けますが、それでも動くものの、必要以上に制限が厳しくなります。
直し方2:代わりに所有権のある String を返す
Section titled “直し方2:代わりに所有権のある String を返す”関数が新しい文字列を作るとき、または呼び出し側を借用に縛りつけたくないときはこれを選びましょう。所有権のある String は呼び出し側のものになるので、追跡するものが何もありません。
fn greeting(name: &str, lang: &str) -> String { if lang == "es" { format!("Hola, {name}!") } else { format!("Hello, {name}!") }}
fn main() { println!("{}", greeting("Ana", "es")); println!("{}", greeting("Sam", "en"));}実行結果:
Hola, Ana!Hello, Sam!この方法では、小さなメモリ確保が1回増えます。ほとんどのプログラムでは、まったく問題ありません。気にしなくて大丈夫です。
直し方3:構造体には所有データを入れる
Section titled “直し方3:構造体には所有データを入れる”title: &str のような構造体のフィールドでも、同じエラーが出ます。
error[E0106]: missing lifetime specifier --> src/main.rs:2:12 |2 | title: &str, | ^ expected named lifetime parameter構造体の場合、いちばんよい直し方はほぼいつも「データを所有する」ことです。&str の代わりに String を、&[T] の代わりに Vec<T> を使いましょう。そうすれば、構造体を好きなだけ長く使えます。
#[derive(Debug)]struct Book { title: String, // &str ではなく、所有する pages: u32,}
fn main() { let book = Book { title: String::from("Dune"), pages: 412 }; println!("{}({}ページ)", book.title, book.pages);}実行結果:
Dune(412ページ)構造体にライフタイムを付けるのは、それがほかの場所にあるデータを短い間だけのぞくビューのときだけにしましょう。たとえば、大きなテキストを読んでいくパーサーのようなものです。その場合は、構造体とフィールドの両方に <'a> を書きます。
struct Excerpt<'a> { text: &'a str, // 下の novel から借りる}
fn main() { let novel = String::from("Call me Ishmael. Some years ago..."); let first = novel.split('.').next().unwrap(); let excerpt = Excerpt { text: first }; println!("抜粋: {}", excerpt.text);}実行結果:
抜粋: Call me Ishmael直し方4:文字列リテラルには 'static を使う
Section titled “直し方4:文字列リテラルには 'static を使う”関数に参照の引数が1つもないと、エラーは「借りてくる元になる値がない」と言います。
error[E0106]: missing lifetime specifier --> src/main.rs:1:22 |1 | fn default_name() -> &str { | ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from"OK" のような文字列リテラルしか返さないなら、&'static str と書きましょう。リテラルはプログラム本体に埋め込まれているので、実行中ずっと生きています。
fn status_text(code: u16) -> &'static str { match code { 200 => "OK", 404 => "Not Found", _ => "Unknown", }}
fn main() { println!("200 の意味は {}", status_text(200)); println!("404 の意味は {}", status_text(404));}実行結果:
200 の意味は OK404 の意味は Not Found'static を使うのは、本物のリテラルや定数のときだけにしましょう。実行中に作られる文字列(format! で作ったものや、ファイルから読んだもの)には 'static は役に立ちません。その場合は String を返します(直し方2)。
よく見かける場面
Section titled “よく見かける場面”- 2つの入力を受け取るヘルパー関数。たとえば
fn lookup(map: &HashMap<String, String>, key: &str) -> Option<&str>です。直し方:'aで戻り値をmapにだけ結びつけます(直し方1)。 - 文字列を持つ構造体。たとえば、パースした入力から作る
User { name: &str }です。直し方:フィールドをStringにします(直し方3)。 - 決まったメッセージを返す関数。たとえば
fn error_label() -> &strです。直し方:&'static strにします(直し方4)。
メソッドについてはうれしいお知らせがあります。fn name(&self) -> &str は、規則3のおかげでライフタイムをまったく書かなくて済みます。
関連するエラー
Section titled “関連するエラー”- E0382: borrow of moved value:所有権に関する別のエラーで、渡してしまった値をあとで使おうとしたときに出ます。
- E0502: mutable borrow while immutably borrowed:ライフタイムが整ったあとに、借用チェッカーが働く場面です。
- E0499: two mutable borrows:同じデータへの
&mutが同時に2つあるときに出ます。 - E0308: mismatched types:たとえば、
&strを返すと書いた関数からStringを返したときに出ます。 - E0515: cannot return reference to local variable:関数の中で作ったデータに直し方1を使おうとしたときに出ます。代わりに所有権のある
Stringを返しましょう(直し方2)。 - コンパイルエラー解説の一覧
理解度チェック
Section titled “理解度チェック”理解度チェック
1. fn first_word(s: &str) -> &str がライフタイムなしでコンパイルできるのはなぜでしょう?
2. 構造体 Config { path: &str } で E0106 が出ました。たいていの場合、いちばんよい直し方はどれでしょう?
3. format! でメッセージを作って返す関数があります。戻り値の型はどれにすべきでしょう?
スコア: 0 / 3
さらに詳しく
Section titled “さらに詳しく”- E0106 — Rust error codes index(英語):このエラーの公式の説明です。
- ライフタイムで参照を検証する — The Rust Programming Language 日本語版:
longestの例や、参照を持つ構造体も含めた、ライフタイムの詳しい解説です。 - Lifetime elision — The Rust Reference(英語):Rust がライフタイムを補うときに使う、正確な規則です。
- 省略 — Rust By Example 日本語版:ライフタイムを書かずに済む場面の短い例です。
- ライフタイム — Rust By Example 日本語版:関数・メソッド・構造体で明示的にライフタイムを書く、実行できる例が見られます。