error: lifetime may not live long enough

error - ライフタイムの不足』のクロージャについての派生パターン。

背景情報

クロージャは関数と異なり、その代入先や実引数の型情報も型推論に利用できる。これを利用して、クロージャは引数のライフタイムについて、省略ルールではなく、そうした周辺情報から推論する戦略を取っている。

この戦略はだいたいうまくいくが、複数のライフタイムについての関係性 (等しいや含む) を示したい場合、『解決策』で紹介する方法が必要で、こちらは少し面倒である。

サンプル

以下では、単純なライフタイムの推測ができずエラーになっている。


fn main() {
    let _closure = |x: &i32| -> &i32 { x };
}

error: lifetime may not live long enough
 --> src\main.rs:2:40
  |
2 |     let _closure = |x: &i32| -> &i32 { x };
  |                        -        -      ^ returning this value requires that `'1` must outlive `'2`
  |                        |        |
  |                        |        let's call the lifetime of this reference `'2`
  |                        let's call the lifetime of this reference `'1`

解決策

解決策はいくつかある。

※ 高階関数は万能だが少し面倒…。

解決策 1

不安定な機能

不安定な機能 closure_lifetime_binder を使う。

欠点: 、ナイトリー版でしか使えない。


#![feature(closure_lifetime_binder)]

fn main() {
    let _closure = for<'a> |x: &'a i32| -> &'a i32 { x };
}

解決策 2

型指定

クロージャ専用の機能を使っていなければ、関数として型指定すればよい。

欠点: クロージャによる変数のキャプチャができない。


fn main() {
    let _closure = (|x| { x }) as fn(&i32) -> &i32;
}

解決策 3

関数化

クロージャの代わりに関数を使えば、同じ構成でも問題にならない。

欠点: クロージャによる変数のキャプチャができない。


fn main() {
    let _closure = function;
}

fn function(x: &i32) -> &i32 {
    x
}

解決策 4

高階関数

クロージャを生成する関数を利用すれば、関数のライフタイム規則を利用できる。

利点: クロージャによる変数のキャプチャを再現するには、関数にその変数を渡せばよい。


fn main() {
    let _closure = make_closure();
}

fn make_closure() -> impl Fn(&i32) -> &i32 {
    |x| x
}