在Rust中,当我借用一个值时,编译器会注意到,但当我替换它时,编译器不会注意到,并发出E0597错误。
给定一个包含引用x
的可变变量,当我用对局部变量的引用替换它的内容时,在局部变量超出作用域之前,我把它替换回原来的。
下面是一段代码:
struct X {payload : i32}
fn main() {
let pl = X{payload : 44};
{
let mut x = &pl;
{
let inner = X{payload : 30};
let tmp = std::mem::replace(&mut x, &inner);
println! ("data ={:?}", x.payload);
let _f = std::mem::replace(&mut x, &tmp);
}
println! ("data ={:?}", x.payload);
}
}
错误为:
error[E0597]: `inner` does not live long enough
--> src/main.rs:9:49
|
9 | let tmp = std::mem::replace(&mut x, &inner);
| ^^^^^^ borrowed value does not live long enough
...
12 | }
| - `inner` dropped here while still borrowed
13 | println! ("data ={:?}", x.payload);
| --------- borrow later used here
For more information about this error, try `rustc --explain E0597`.
编译器注意到我将inner
的引用赋给x
,但忽略了这样一个事实,即当inner
仍然有效时,我再次将此引用替换为对pl
的原始引用。
预期输出应为:
data =30
data =44
我哪里做错了?
2条答案
按热度按时间brjng4g31#
我已经解决了这个问题。不幸的是,它是一个编译器错误或限制。
语义上等价的代码,代码的学分属于发布了部分答案的人,但随后将其删除。
此代码将产生相同的错误。
然而,一个小的调整将使它编译没有错误或警告。
这让我相信有一个编译器bug,因为现在编译器发现
inner
的生存期与x
的生存期解耦了。当你把内部代码块放到一个单独的函数中时,问题又回来了。所以这只是一个例子,Rust编译器有一些优化代码路径,捕捉到了极端情况。
Frederico提供了一种方法,可以将“无尽”的生命周期硬塞到
inner
的生命周期中,使其更大,即使这意味着使用不安全的生命周期。8i9zcol22#
当分析Rust代码时,编译器是保守的,并且拒绝它不能确定是否存在内存错误的程序。
特别是,当编译器检查代码中的
let tmp = ...
时,它会注意到您正在存储一个指向短期示例的引用(inner
)转换为引用在 *inner
之后 * 使用的(pl
)超出范围。编译器在此停止并报告错误,说你 * 可能 * 创建了一个悬空引用。它没有考虑到在你恢复原始的pl
之后。目前用于分析的生存期抽象不够精确。在Rust中,你必须构造你的代码,这样编译器才能轻松地检查它,这意味着,比如,你必须在外部作用域声明
inner
,或者你使用Rc
这样的类型来代替引用,它用库中执行的运行时检查来代替编译时检查。如果你真的想使用不安全的代码,你可以按照下面的方法来做,但这是非常不建议的。你可以手动检查代码是否有未定义的行为,这是很难做到的,因为目前还没有完整的描述什么是未定义的行为。
**警告:**下面的列表并不详尽。对于不安全代码中允许和不允许的行为,Rust的语义没有正式模型,因此可能有更多的行为被认为是不安全的。下面的列表只是我们确定的未定义行为。请在编写不安全代码之前阅读Rustonomicon。