我遇到了生命周期错误,我无法解释为什么编译器会发出该错误。我需要这个(效果很好):
fn iter<'a>() -> impl Iterator<Item = &'a f64> {
[3.14].iter()
}
但是,当我尝试使用使用from_bits
从特定字节表示形式转换成的float值时,如下所示:
fn iter<'a>() -> impl Iterator<Item = &'a f64> {
[f64::from_bits(0x7fffffffffffffff)].iter()
}
它给我“ 创建一个在使用中仍可释放的临时文件”。游乐场here(稳定1.45.2)。
我的理由是,由于f64
是Copy
类型的(并且如果我使用常量值,它的确可以按预期工作),这应该可以工作,因为不应该对此值执行释放。 >
所以问题是为什么编译器在第二种情况下会发出错误?
感谢您的指导和解释!
P.S。我需要在 references 上进行迭代的迭代器,因为它非常适合我的其他API。
答案 0 :(得分:2)
您的代码有两个相关问题:
参考仅能指向它们所指向的数据。方法调用f64::from_bits(0x7fffffffffffffff)
的结果是一个临时对象,该对象超出了表达式末尾的范围。无法从函数返回对临时值或局部变量的引用,因为一旦函数返回,引用的值将不再有效。
Copy
特性或对象是否存储在堆上与以下事实完全无关:不能再引用超出范围的值。在函数内部创建的任何值将超出函数主体末尾的范围,除非您通过返回值将其移出函数。但是,您需要移动所有权的值才能使其正常工作-您不能简单地返回引用。
由于您无法返回对在函数内部创建的任何值的引用,因此返回值中的任何引用都必须引用通过函数参数传入的内容。这意味着返回值中任何引用的 lifetime 必须与传递给函数的某个引用的生存期相匹配。这使我们进入第二点–仅在返回类型中出现的生命周期参数始终是错误。生命周期参数由调用代码选择,因此从本质上讲,您的函数将返回一个可在调用者选择的任意时间内生存的引用,仅当该引用引用的寿命与程序一样长的静态数据时,这才可能
这也解释了为什么第一个示例有效。文字[3.14]
定义一个常量静态数组。该数组的寿命与程序一样长,因此您可以返回具有任意生存期的引用。但是,通常可以通过明确指定静态生存期来明确说明发生的情况来表达这一点:
fn iter() -> impl Iterator<Item = &'static f64> {
[3.14].iter()
}
仅在返回值中出现的生命周期参数不再有用。
那么您如何解决问题?由于您的iter()
函数不接受任何参数,您可能需要返回一个拥有类型的迭代器。
答案 1 :(得分:0)
它是Copy
,它在语义上被复制,这确实是个问题,因为它在语义上仅存在于堆栈中,直到函数返回为止,现在,从语义上来说,引用指向堆栈外部的内存,并且很可能很快就会被覆盖,如果Rust允许,则会导致不确定的行为。
最重要的是,from_bits
不是const,这意味着您不能在编译时转换静态值,这是一个运行时操作。为什么要在每次已经知道该值时都进行转换?
为什么这样呢?
这目前在所有方面都与transmute ::
(v)相同 平台。
如果您查看transmute
,则会发现:
transmute在语义上等效于一种类型的按位移动 变成另一个。它将位从源值复制到 目标值,然后忘记原始值。相当于C的 就像是transmute_copy一样。
虽然生成的代码确实可能只是对静态值的简单重新解释,但Rust不能在语义上允许将该值移到堆栈上,然后丢弃,而引用仍指向该值。
解决方案。
由于要返回NaN
,因此您应该执行与第一个示例相同的操作:
fn iter<'a>() -> impl Iterator<Item = &'a f64> {
[f64::NAN].iter()
}
这将直接在静态切片上进行迭代,并且不会出现任何问题。