0

我遇到了一个生命周期错误,我无法解释为什么它是由编译器发出的。我需要这个(效果很好):

fn iter<'a>() -> impl Iterator<Item = &'a f64> {
    [3.14].iter()
}

但是,当我尝试使用从特定字节表示转换而来的浮点值时from_bits,如下所示:

fn iter<'a>() -> impl Iterator<Item = &'a f64> {
    [f64::from_bits(0x7fffffffffffffff)].iter()
}

它给了我“创建一个临时的,在使用中被释放”。这里的游乐场(稳定的 1.45.2)。

我的理由是,因为f64isCopy类型(如果我使用常量值,它确实可以按预期工作),这应该可以工作,因为不应该对该值执行释放。

所以问题是为什么编译器会在第二种情况下发出错误?

感谢您的任何指示和解释!

PS 我需要迭代器而不是引用,因为它非常适合我的其他 API。

4

2 回答 2

2

您的代码有两个相关的问题:

  1. 您返回对在函数体末尾超出范围的临时对象的引用。
  2. 您的返回类型包含一个未绑定到任何函数输入的生命周期参数。

引用只能与它们指向的数据一样长。方法调用的结果f64::from_bits(0x7fffffffffffffff)是一个临时对象,它在表达式末尾超出范围。从函数返回对临时值或局部变量的引用是不可能的,因为一旦函数返回,所引用的值将不再存在。

特征,Copy或者对象是否存储在堆上,与超出范围的值不能再被引用这一事实完全无关。在函数内部创建的任何值都将在函数体末尾超出范围,除非您通过返回值将其移出函数。但是,您需要移动值的所有权才能使其正常工作——您不能简单地返回引用。

由于您无法返回对函数内部创建的任何值的引用,因此返回值中的任何引用都必须引用通过函数参数传入的内容。这意味着返回值中任何引用的生命周期都需要与传递给函数的某个引用的生命周期相匹配。这让我们来到了第二点——一个只出现在返回类型中的生命周期参数总是一个错误。生命周期参数由调用代码选择,因此您实际上是在说您的函数返回一个在调用者选择的任意时间内存在的引用,这只有在引用引用与程序一样长的静态数据时才有可能.

这也解释了为什么您的第一个示例有效。字面量[3.14]定义了一个常量静态数组。该数组将与程序一样长,因此您可以将具有任意生命周期的引用返回给它。但是,您通常会通过显式指定静态生命周期来表达这一点,以明确发生了什么:

fn iter() -> impl Iterator<Item = &'static f64> {
    [3.14].iter()
}

只出现在返回值中的生命周期参数是没有用的。

那么你如何解决你的问题呢?您可能需要在拥有的类型上返回一个迭代器,因为您的iter()函数不接受任何参数。

于 2020-08-17T08:37:00.960 回答
0

它是Copy,它在语义上被复制,这确实是问题所在,因为它在语义上只存在于堆栈中,直到函数返回,引用现在,在语义上,指向堆栈外的内存并且很可能很快被覆盖,如果 Rust 允许,那将导致未定义的行为。

最重要的是,from_bits不是 const,这意味着您无法在编译时转换静态值,它是运行时操作。既然已经知道值,为什么还要每次都转换?


为什么会这样呢?

from_bits

这目前与所有平台上的 transmute::<u64, f64>(v) 相同。

如果你看一下transmute,你会发现:

transmute 在语义上等同于将一种类型按位移动到另一种类型。它将源值中的位复制到目标值中,然后忘记原始值。它在底层相当于 C 的 memcpy,就像 transmute_copy 一样。

虽然生成的代码确实可能只是对静态值的简单重新解释,但 Rust 在语义上不能允许将该值移动到堆栈上,然后丢弃,而引用仍然指向它。


解决方案。

既然你想返回 a NaN,你应该做你在第一个例子中做的同样的事情:

fn iter<'a>() -> impl Iterator<Item = &'a f64> {
    [f64::NAN].iter()
}

这将直接遍历静态切片,不会有任何问题。

于 2020-08-17T08:37:30.427 回答