为什么将静态引用转换为const会返回对临时变量的引用?

4
在Rust中,我有以下的代码:
pub trait Test: Sized {
    const CONST: Self;
    fn static_ref() -> &'static Self {
        &Self::CONST
    }
}

我的期望是,由于const'static,那么我应该能够获取一个同样是'static的引用。然而,编译器给出了以下错误:

error[E0515]: cannot return reference to temporary value
   --> file.rs:9:9
    |
  9 |         &Self::CONST
    |         ^-----------
    |         ||
    |         |temporary value created here
    |         returns a reference to data owned by the current function

在这里引入了一个临时变量,如何实现的?

另外,似乎有些情况下对常量取引用是可行的。以下是一个具体的短例子,使用稍微不同的Test实现。

pub trait Test: Sized {
    fn static_ref() -> &'static Self;
}

struct X;

impl Test for X {
    fn static_ref() -> &'static Self {
        &X
    }
}

看起来你正在尝试将CONST的生命周期延长到函数的生命周期之外。当你退出函数时,对CONST的引用就不再有效了。 - miimote
3个回答

13
在Rust中,常量是编译时常量,不是真正具有内存位置的变量。Rust编译器可以在使用时替换常量的实际值。如果您获取这种值的地址,则会得到临时地址。
Rust还有一个静态变量的概念。这些变量实际上具有整个程序运行期间一致的内存位置,并且对静态变量进行引用确实会导致带有'static 生命周期的引用。
另请参见:

这似乎与一些文档不一致。例如,我可以这样做...impl Test for X { fn static_ref() -> &'static Self { &X{} } } 在这个例子中,值X{}是一个常量,但我却对它取了一个引用。 - Zachary Burns
我会编辑问题以添加这个额外的细节。 - Zachary Burns
@ZacharyBurns,我不太能理解你在评论中给出的例子。如果这个例子不足以说明问题,我建议您提出一个新的问题,或者至少提供一个可供参考的示例链接,以便我们跟进。另外,如果您认为文档有误,提供一个指向正确文档的指针也会很有帮助。 - Sven Marnach
我已经编辑了我的问题,以澄清有时如何引用常量。我正在寻找文档,并将很快更新评论。 - Zachary Burns
什么是temporary? - joel
@joel 临时变量是表达式中的匿名中间结果,仅在当前语句的生命周期内存在。 - Sven Marnach

5

当你定义一个特征时,定义必须对所有可能的实现都有意义。

如果没有一个失败的例子,问题可能不会立即清晰。因此,假设您有这样一种类型:

struct MyStruct;
impl MyStruct {
    const fn new() -> Self {
        MyStruct
    }
}

您尝试像这样实现trait:

impl Test for MyStruct {
    const CONST: Self = MyStruct::new();
}

这个不起作用,因为 static_ref 的实现现在看起来像这样:

fn static_ref() -> &'static Self {
    // &Self::CONST
    &MyStruct::new()
}

它在函数内部创建一个值并尝试返回它。这个值是静态的,因此'static生命周期是无效的。


但是,稍加改动,你就可以让一些东西起作用:

pub trait Test: Sized + 'static {
    // This is now a reference instead of a value:
    const CONST: &'static Self;
    fn static_ref() -> &'static Self {
        Self::CONST
    }
}

struct MyStruct;
impl MyStruct {
    const fn new() -> Self {
        MyStruct
    }
}

impl Test for MyStruct {
    const CONST: &'static Self = &MyStruct::new();
}

这是因为CONST已经是一个'static引用,所以函数可以直接返回它。所有可能的实现都必须能够获取到Self'static引用来实现特性,因此不再存在引用任意本地值的问题。

1
这里使用的机制是静态提升。 请参见RFC 1414
以下是一句引用:
在函数体块内部: - 如果获取了一个对constexpr右值的共享引用。(&) - 并且constexpr不包含UnsafeCell { ... }构造函数。 - 并且constexpr不包含返回包含UnsafeCell类型的const fn调用。 - 那么,将该值转换为静态内存位置,而不是将其翻译成堆栈插槽,并给出结果引用的“静态”生命周期。

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