できない.dev

Rust で「missing lifetime specifier」が解消できない

参照を返す関数や参照を持つ構造体で E0106 が出るのは、コンパイラがその参照をどの入力から借りたのか決められないためである。
ライフタイム引数を明示するか、借用をやめて所有権のある型を返すかで解決する。

公開:

要約

error[E0106]: missing lifetime specifier は「参照を書いたが、その参照がどれだけ生きるのかコンパイラに伝わっていない」という意味である。
借用チェックに失敗しているのではなく、シグネチャに情報が足りないだけなので、ライフタイム引数を書けば通る。

fn longest(x: &str, y: &str) -> &str {
    if x.len() > y.len() { x } else { y }
}
error[E0106]: missing lifetime specifier
 --> src/main.rs:1:33
  |
1 | fn longest(x: &str, y: &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 `x` or `y`

戻り値が xy のどちらから借りたのかは、本文を読まないと分からない。
Rust は本文を見ずにシグネチャだけで判断する設計なので、ここで止まる。

よくある原因

  1. 入力の参照が 2 つ以上ある: Lifetime elision(新しいタブで開く) の省略規則で戻り値のライフタイムが自動的に決まるのは、入力の参照が 1 つのときか、メソッドで &self があるときだけである。
    それ以外は明示が必要になる。
  2. 構造体が参照を持っている: struct Parser { input: &str } のように書くと、その構造体が借用元より長生きしないことを保証できないため同じエラーになる。
  3. 借用元が存在しない: fn make() -> &str のように入力に参照が無い場合、返せる参照は 'static なものしか無い。
    関数内で作った値を返したいなら参照では表現できない。
  4. impl 側の書き忘れ: 型にライフタイム引数を足しても、impl Parser のままだと今度は impl 側で同じエラーが出る。

解決策

1. 借用元と戻り値を同じライフタイムで結ぶ

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() { x } else { y }
}

'a は「x と y の短いほうが生きている間」を表す。
呼び出し側はその範囲でだけ戻り値を使える。

2. 片方だけから借りるなら片方だけに付ける

戻り値が常に第 1 引数から借りるのであれば、第 2 引数にライフタイムは要らない。

fn prefix<'a>(text: &'a str, sep: &str) -> &'a str {
    text.split(sep).next().unwrap_or(text)
}

必要以上に引数を同じ 'a で縛ると、呼び出し側の自由度が下がる。

3. 構造体と impl の両方に宣言する

struct Parser<'a> {
    input: &'a str,
}
 
impl<'a> Parser<'a> {
    fn new(input: &'a str) -> Self {
        Parser { input }
    }
 
    fn rest(&self) -> &str {
        self.input
    }
}

rest のようにメソッドで &self がある場合は省略規則が働くので、戻り値のライフタイムは書かなくてよい。

4. 借用をやめる

関数内で組み立てた文字列を返したいときは、参照ではなく所有権のある型にする。

fn banner(name: &str) -> String {
    format!("== {name} ==")
}

'static を付ければコンパイルが通る場合もあるが、それはリテラルなど本当にプログラム終了まで生きる値に限られる。
迷ったら String を返すほうが安全である。

この記事は役立ちましたか?