web-dev-qa-db-ja.com

変数ではなくローカルリテラルへの参照を返すことができるのはなぜですか?

このコードがコンパイルされるのはなぜですか?

_fn get_iter() -> impl Iterator<Item = i32> {
    [1, 2, 3].iter().map(|&i| i)
}

fn main() {
    let _it = get_iter();
}
_

_[1, 2, 3]_はローカル変数であり、iter()はそれを借用します。戻り値はローカル変数への参照を保持しているため、このコードはコンパイルしないでください。

21
Boiethios

あなたの例では、[1, 2, 3]はローカル変数としてではなく、静的変数として扱われます。

このコードを見てみましょう:

fn foo() -> &'static [i32] {
    &[1, 2, 3]
}

これはうまくいきます!

少し前に、 RFC 1414:Rvalue Static Promotion がマージされました:「constexprの右辺値をスタックスロットではなく静的メモリ内の値に昇格させます」。これは、基本的にあなたが書くすべてのリテラルが永遠に生きることができることを意味します。したがって、let _: &'static i32 = &42;のようなものも機能します!

リテラル配列の使用を避けると、予想されるエラーが表示されます。

fn bar() -> impl Iterator<Item = i32> {
    vec![1, 2, 3].iter().map(|&i| i)
}

ここで、「vが十分に長く存続していません」というエラーが発生します。

これは整数や配列に限定されません。これは、リテラルのみで構成されるリテラルに広く適用されます。

fn promote_integer() -> &'static i32 {
    &42
}
fn promote_float() -> &'static f64 {
    &42.42
}
fn promote_str() -> &'static str {
    "Hello World!"
}
struct Foo(char);

fn promote_struct() -> &'static Foo {
    &Foo('x')
}

リテラルを超えて、これは標準ライブラリ内のtiny個の関数に対しても機能します しかしこれらはおそらく間違いでした 。任意のconst関数の結果を自動的にstaticにプロモートできるかどうかの決定は、まだ オープントピック です。

30