如何初始化具有生命周期的变量?

24

我有下面的代码,但不知道如何让它工作:

fn new_int<'a>() -> &'a isize {
    &5
}

fn main() {
    let x = new_int();
}

或者另一种尝试:

fn new_int<'a>() -> &'a isize {
    let a: &'a isize = &5;
    a
}

fn main() {
    let x = new_int();
}

1
我不知道你想做什么,但你正在尝试创建一个引用到一个生命周期比'a'更小的值。它具有函数体的生命周期。 - oli_obk
我正在尝试使用生命周期参数将该变量暴露给外部。如果可能的话。 - Hinogary
1
该变量在函数体外已经失效。如果您仍然访问它,将会覆盖其他内存。错误信息(请参见http://is.gd/ju7hFZ)明确告诉您这一点。 - oli_obk
1
由于static提升(请参见为什么我可以返回对本地文字的引用,但不能返回变量?),这些函数现在已经编译成功。 - trent
3个回答

30
你不能这样做。lifetime参数不能让你选择一个值存活的时间有多长,它只能让编译器知道两个或更多引用“相关”,并且期望共享相同的lifetime。
一个函数(比如你例子中的new_int)可以通过两种方式分配内存:
1. 在函数本身分配的区域中局部地分配,在从函数返回时销毁(栈) 2. 在所有函数都共享的内存区域动态分配(堆)
引用(&)是指向内存区域的指针。它可以指向本地栈或堆。由于动态分配在性能方面的成本要比写入栈高得多,因此Rust默认使用栈(您必须使用Box来执行动态分配)。
因此,简而言之,这就是为什么你的代码是非法的原因:
fn new_int<'a>() -> &'a isize {
    let a: &'a isize = &5; // write 5 on the function's local stack
    a // return a pointer to that area of memory
} // the function ends and its stack (where I wrote 5) is destroyed
  // so the pointer I'm trying to return is no longer valid

你可以返回值

fn new_int() -> isize {
    5
}

fn main() {
    let a = new_int(); // the value 5 (not a pointer) is copied into a
}

或者执行动态分配(如果您实际上正在使用大型结构,则 isize 可能过于复杂,但可能是有意义的)

fn new_int() -> Box<isize> {
    Box::new(5) // a Box allocates memory and writes in the heap
}

fn main() {
    let a = *new_int();
}

另外,您可以在函数外部分配内存,并在函数中进行修改。对于原始类型,通常不这样做,但在某些情况下(例如数据流),这是有意义的:

// new_int does not return anything. Instead it mutates
// the old_int in place
fn new_int(old_int: &mut isize) {
    *old_int = 5;
}

fn main() {
    let mut a = 2; // memory for an int is allocated locally
                   // in main()
    new_int(&mut a); // a mutable reference to that memory is passed
                     // to new_int, that overwrites it with another value
}

在这种特定情况下(即您的函数始终返回5或其他静态已知值,而不是由函数动态计算的值),您还可以返回具有 'static 寿命的引用:如下评论 @dk mentions in the comment below 提到。
fn new_int<'a>() -> &'a isize {
    static FIVE: isize = 5;
    &FIVE
}

你可以阅读有关 'static 在 Rust 参考手册中' 的更多信息。
从 Rust 1.21 开始,这种"静态提升"现在已经自动为您执行,您的原始代码将会编译。它创建了等效于 static FIVE 的内容。

5
放弃我正在进行的回答,但我想指出第三种选择:如果它总是返回5,或者任何有限的一组答案,您可以返回对静态变量的借用引用:static FIVE: isize = 5;,然后将&FIVE作为函数的结果。 - DK.

9
一种理解为什么的另一种方式是:
fn new_int<'a>() -> &'a isize {
    &5
}

无法工作的原因如下。 'a是函数的生命周期参数;也就是说,实际值由调用方选择,而不是函数本身。例如,调用方可以选择'static生命周期:
let i: &'static isize = new_int();

然而,&5不能拥有'static生命周期,因此该函数被拒绝。

换句话说,这种声明本质上是在说“我可以给你一个你想要的任何生命周期的引用”。当然,只有当函数返回的引用是'static生命周期时才有效,这是可能的最长寿命。顺便说一下,这就是DK.所说的。


4

生命周期仅描述代码已经执行的操作,不会以任何方式影响代码的行为。

它们不是指示将某些东西保持活动状态所需的指令,而是一致性检查,确保代码实际执行其所说的内容。

实际上,Rust在检查完生命周期之后会从代码中删除所有生命周期信息,然后编译代码而没有任何关于生命周期的知识。

变量在其作用域结束时被销毁,这就是它们的生命周期。您不能声明它们不会这样做。


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