为什么变量作用域取决于定义顺序?

3

我有一个基本(可能很愚蠢的)所有权问题。 我正在尝试从包含在Some(String)中的String值中创建&str向量。 我使用一个中间变量来存储提取/解包的String,并且似乎需要在向量之前定义这个中间变量才能满足借用检查器:

工作代码:

fn main() {
    let a = Some("a".to_string());

    let mut val = String::new();
    let mut v = Vec::<&str>::new();

    if a.is_some() {
        val = a.unwrap();
        v.push(&val[..]);
    }
    println!("{:?}", val);
}

无法运行的代码:

fn main() {
    let a = Some("a".to_string());

    let mut v = Vec::<&str>::new();
    let mut val = String::new();

    if a.is_some() {
        val = a.unwrap();
        v.push(&val[..]);
    }
    println!("{:?}", val);
}

编译器错误:

<anon>:9:17: 9:20 error: `val` does not live long enough
<anon>:9         v.push(&val[..]);
                        ^~~
<anon>:4:35: 12:2 note: reference must be valid for the block suffix following statement 1 at 4:34...
<anon>:4     let mut v = Vec::<&str>::new();
<anon>:5     let mut val = String::new();
<anon>:6 
<anon>:7     if a.is_some() {
<anon>:8         val = a.unwrap();
<anon>:9         v.push(&val[..]);
        ...
<anon>:5:32: 12:2 note: ...but borrowed value is only valid for the block suffix following statement 2 at 5:31
<anon>:5     let mut val = String::new();
<anon>:6 
<anon>:7     if a.is_some() {
<anon>:8         val = a.unwrap();
<anon>:9         v.push(&val[..]);
<anon>:10     }
        ...
error: aborting due to previous error
playpen: application terminated with error code 101

围栏代码

问题是:为什么我要在向量v之前定义val变量?在我看来,val的作用域与v的作用域相同,或者我漏掉了什么?

1个回答

3

绑定按照声明的反向顺序进行删除,也就是最近声明的东西首先被销毁。具体来说,在代码中,未能正常工作的是在v的析构函数之前运行了val的析构函数。如果没有仔细考虑Vec<&str>::drop()的功能,这是不安全的:例如,它可能尝试查看它包含的字符串切片的内容,尽管它们派生自已经被销毁的String

Vec实际上并没有那么做,但是其他合法的类型会执行类似的操作。以前,对于包含生命周期/借用指针的类型来说,安全地实现Drop是不可能的。一个相对较新的变化通过引入这些额外限制使其变得安全。

请注意,如果您声明了let v, val;let val, v;并稍后分配,两个绑定确实具有相同的生命周期,因此拥有相同生命周期的两个变量并非不可能。


谢谢。在同一行定义两个变量可以使其独立于顺序而工作,例如:let (mut v, mut val) = (Vec::<&str>::new(), String::new()); - mhristache
现在出现了一个有趣的问题:当vval同时声明时,它们的销毁顺序是什么?因为你仍然需要在v之后销毁val,编译器是否能推断出正确的顺序呢? - Matthieu M.
@MatthieuM。好问题。这个变化有两个部分,一个是丢弃规则(线性类型严格地比它内部的所有引用更长寿),另一个是let x; let y; ... 被解析为 let x; { let y; { ... }}。后者在技术上是语法糖,它使得满足第一部分限制更容易。我最好的猜测是"结构体严格比某个引用更长寿"的要求不适用于Vec,可能是因为它仍然使用#[unsafe_destructor]或者因为它没有生命周期参数?那么两者具有相同的生命周期将是可行的。 - user395760

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