为什么我可以返回对本地文字的引用但不能返回变量的引用?

49

为什么这段代码能够编译?

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个回答

66
在你的例子中,[1, 2, 3]并不被视为局部变量,而是静态变量!
让我们看一下这段代码:
fn foo() -> &'static [i32] {
    &[1, 2, 3]
}

这很有效!

不久前,RFC 1414:Rvalue Static Promotion被合并:“将constexpr rvalues提升为静态内存中的值,而不是堆栈插槽中的值”。这意味着你编写的基本上所有文字都可以永久存在。因此,像let _: &'static i32 = &42;这样的东西也可以工作!

如果我们避免使用文字数组,就可以看到预期的错误:

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

这里出现了“v does not live long enough”错误。
这不仅限于整数或数组,它广泛适用于任何仅由文字组成的文字表达式:
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')
}

除了字面量外,标准库中也有极少数函数可以这样做,但这可能是一个错误。决定任意const函数的结果是否可以自动提升为static仍然是一个开放性话题

1
@Boiethios,那是一个有趣的想法。你找到了相关讨论并可以提供链接吗? - Cosmo
6
它们并不过时。这仅适用于字面值。有时候你需要一个必须计算的常量。 - Peter Hall
4
@Boiethios 在静态内存中存储数据存在一些缺点,特别是当数据很大且不需要在程序的整个生命周期内使用时。肯定有时候你应该选择加入这种行为。 - Peter Hall
使用&42是否会强制编译器将42存储在静态内存中,而不是将其作为内联处理? - Alexey
2
@Alexey 是的,这就是语言模型所说的。然而,优化器仍然可以随后内联该值。 - Lukas Kalbertodt
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接