在函数作用域内创建的引用是否可以返回?

7

我有一个非常简单的程序:

fn f<'a>() -> &'a i32 {
    &1
}

fn main() {
    println!("{}", f());
}

它无法编译(部分输出被省略):

$ rustc test.rs
test.rs:2:6: 2:7 error: borrowed value does not live long enough
test.rs:2     &1

我理解为什么它会失败。

  1. 我不知道如何返回在函数作用域内创建的引用。有办法可以做到吗?
  2. 为什么生命周期不能被省略为单个返回?

编辑:我更改了标题,因为它暗示返回装箱类型会有所帮助,但事实并非如此(请参见答案)。


1
我理解为什么它会失败 - 我不认为这完全正确 :-) 如果你真的理解了,你也应该明白为什么你不能这样做。作为一个思想实验,如果你用 'static 参数化 'a 调用 f,会发生什么? - Shepmaster
3个回答

3
截至 Rust 1.21,一项名为rvalue static promotion的新功能意味着问题中的代码现在可以编译。在这种情况下,由于1是一个常量,编译器将其提升为静态引用,这意味着返回的引用具有“static”生命周期。函数展开后看起来像这样:
fn f<'a>() -> &'a i32 {
    static ONE: i32 = 1;
    &ONE
}

这适用于任何编译时常量,包括结构体:
struct Foo<'a> {
    x: i32,
    y: i32,
    p: Option<&'a Foo<'a>>
}

fn default_foo<'a>() -> &'a Foo<'a> {
    &Foo { x: 12, y: 90, p: None }
}

但这段代码将无法编译:
fn bad_foo<'a>(x: i32) -> &'a Foo<'a> {
    /* Doesn't compile as x isn't constant! */
    &Foo { x, y: 90, p: None }
}

2
将引用进行包装并不会有所帮助。在大多数情况下,Box 与未经包装的 T 几乎完全相同,包括所有权和生命周期问题。根本问题在于,局部变量将在函数返回时停止存在。因此,对局部变量的引用将指向已被释放的内存,而调用函数获取该引用时可能会出现问题。在引用周围包裹包装纸并不能解决这个问题。
我假设这是从您正在遇到问题的真实程序中精简出来的示例。由于缺乏信息,我无法为其提供有针对性的建议,但一般来说,最好通过值返回事物(即在此情况下只需“->i32”)而不是引用。

你是对的,Box 不会有帮助。我错了,认为它会充当引用计数器。 - kopiczko

2
由于Rust使用RAII风格的资源管理,一旦程序离开一个作用域,所有未移动的作用域内的值都将被销毁。为了保证引用的有效性,该值必须存在某处。因此,要么将该值直接返回(如果您担心会有额外的副本,则不用担心,因为该副本会被优化掉),要么将其封装到箱子中并返回该箱子。除非您返回一个静态分配的字符串作为&str ,否则您根本无法返回一个“新”的(对调用方而言)引用:
fn f<'a>() -> &'a str {
    "yo"
}

将静态值返回或将其作为方法返回字段,是使f有效的唯一方法吗?还是定义函数fn f<'a>() -> &'a T的另一个用例?复制优化是否有文档记录,我如何确保它会被执行? - kopiczko
@kopiczko 有一种非常晦涩的情况,其中 fn foo<'a>() -> &'a T&'static 更灵活,但与其返回值无关。至于复制:复制 i32 的成本与复制 &i32 相同或更低!实际上,大多数类型都非常小,通常不值得花费时间去担心它被复制的频率。 - user395760
@delnan 我同意担心复制i32是毫无意义的。但是假设我有一个包含20个字段的结构体,并且根据条件在循环中创建了它1M次。我想提取创建函数以保持DRY原则,这时候担心复制就有意义了,对吧? - kopiczko
可能是有道理的。 (然而,假设20 * 8 = 160字节的一份副本仍然比您实际执行的任何工作要便宜得多。)通过RVO、内联和其他优化(这些优化都不是保证的,因为它们是优化,但绝对常见),从函数返回的一个理论上的副本几乎肯定不会被执行。在获得硬数据之前,您不应该担心!!! - user395760
“创建一个对象并通过值返回它”的函数是一个广泛使用的模式。因此,在发布版本中,它几乎可以保证得到优化。” - John
显示剩余2条评论

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