为什么在浮点数组上调用iter会给出";临时值被释放";当使用f64::from_bits时



我遇到了一个终身错误,我无法解释编译器发出该错误的原因。我需要这个(效果很好):

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)。

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

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

谢谢你的指点和解释!

附言:我需要引用上的迭代器,因为它非常适合我的另一个API。

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

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

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

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

由于不能返回对函数内部创建的任何值的引用,因此返回值中的任何引用都必须引用通过函数参数传入的内容。这意味着返回值中任何引用的生存期都需要与传递给函数的某个引用的生存期相匹配。这就引出了第二点——只出现在返回类型中的生存期参数总是一个错误。lifetime参数是由调用代码选择的,因此您本质上是在说,您的函数返回一个引用,该引用的有效期为调用方选择的任意时间,只有当该引用引用的静态数据与程序的有效期一样长时,这才有可能。

这也解释了为什么你的第一个例子有效。文字[3.14]定义了一个常量静态数组。这个数组将和程序一样长,所以你可以返回具有任意生存期的引用。然而,你通常会通过显式指定静态生存期来表达这一点,以明确发生了什么:

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

只出现在返回值中的生存期参数永远都没有用处。

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

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

除此之外,from_bits不是const,这意味着您不能在编译时转换静态值,它是一个运行时操作。既然您已经知道值,为什么每次都要转换?


为什么会出现这种情况?

from_bits:

这当前与transmite::<u64、f64>(v) 在所有平台。

如果您查看transmute,您会发现:

transmite在语义上等价于一种类型的逐位移动变成另一个。它将源值中的位复制到目的地值,然后忘记原来的值。它相当于C引擎盖下的memcpy,就像transmite_copy一样。

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


解决方案。

既然你想返回一个NaN,你就应该做与第一个例子相同的事情:

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

这将直接在静态切片上迭代,不会出现任何问题。

最新更新