Почему я могу вернуть ссылку на локальный литерал, но не переменную?

Почему этот код компилируется?

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

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

[1, 2, 3] - локальная переменная, и iter() занимает ее. Этот код не должен компилироваться, потому что возвращаемое значение содержит ссылку на локальную переменную.

Ответ 1

В вашем примере [1, 2, 3] не обрабатывается как локальная переменная, а как статическая!

Давайте посмотрим на этот код:

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

Это работает!

Некоторое время назад RFC 1414: Статическое продвижение Rvalue было объединено: "Повышать значения 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_char() -> &'static char {
    &'c'
}
struct Foo(char);

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

Помимо литералов, это также работает для небольшого числа функций в стандартной библиотеке, но они, вероятно, были ошибкой. Решение о том, можно ли автоматически преобразовать результат произвольных const функций в static, остается открытой темой.