为什么借用的顺序在Rust中很重要?

7
fn say_hello(s: &str) {
    println!("Hello {}", s);
}

为什么这个能够工作?
fn main() {
    let mut name = String::from("Charlie");
    let x = &mut name;
    say_hello(x);
    name.push_str(" Brown");
}

但是这个不行?

fn main() {
    let mut name = String::from("Charlie");
    let x = &mut name;
    name.push_str(" Brown");
    say_hello(x);
}

我所做的只是交换了这两个函数的顺序,但似乎在两种情况下x都被可变地借用了名称,并且push_str在两种情况下也被可变地借用了名称,那么为什么第一个例子编译通过?

如果我删除对say_hello()的调用,为什么这两个函数的顺序不重要,即使仍然存在两个可变借用?

编辑: 这个和那个是否相似?

fn change_string(s: &mut String) { // s is mutably borrowed but isn't used yet
    println!("{}", s.is_empty()); // so the scopes don't overlap even though is_empty is making an immutable borrow?
    s.push_str(" Brown");
}

1
关于您的编辑:可变引用(s)可以重新借用为不可变引用(is_empty()self),但是在不可变引用存在时无法使用可变引用,但由于它仅在s.is_empty()表达式期间存在,因此在此之后使用s并不是问题。 - kmdreko
2个回答

10

Rust的借用规则之一是,可变引用是互斥的。也就是说,在x存活期间,name不能被使用。

那么,为什么即使x仍然在作用域内,第一个示例代码也能编译通过呢?因为Rust还有非词法生命周期(non-lexical lifetimes),这意味着x在最后一次使用后停止“存在”。

fn main() {
    let mut name = String::from("Charlie");
    let x = &mut name;
    say_hello(x);            // "x"s lifetime ends here, releasing the exclusive borrow
    name.push_str(" Brown"); // "name" can be used again
}

提及NLL(非词法生命周期)是很重要的,因为它是第一个例子可以编译的原因(在Rust 2018中)。在不支持NLL的Rust 2015中,两个例子都无法编译。 - Daniel

5

因为在第一个情况中,两个可变借用的作用域不重叠,在任何给定时间点上你只有一个借用;但是在第二个情况中,它们重叠,这意味着在某个特定时间点上你有多个可变借用,这是不允许的:

第一个情况:

fn main() {
    let mut name = String::from("Charlie");
    let x = &mut name;
    say_hello(x);             // the mutable borrow ends here
    name.push_str(" Brown");  // a new mutable borrow
}

第二种情况:

fn main() {
    let mut name = String::from("Charlie");
    let x = &mut name;
    name.push_str(" Brown");  // the first mutable borrow is still 
                      // alive, you have two mutable borrows here
    say_hello(x);       // the first mutable borrow ends here
}

我认为你还应该指明这是一个可变借用,并且在 Rust 中不允许保持两个可变借用同时存在。 - weegee
哇!从未知道变量的生命周期一直持续到最后一次使用,而不是作用域结束。 - Alexey Larionov
@AlexLarionov 是的,它很容易被忽略,但一旦你弄清楚了,它非常整洁,我个人认为。 - Psidom
我的编辑器中的函数是否类似于只要作用域不重叠,就可以进行可变借用和不可变借用或进行两个可变借用,还是有所不同? - oberblastmeister
@Rafael 在你修改的函数中,你并没有借用任何东西,而是使用了同一个引用。但我对此没有很好的解释。如果你问一个不同的问题,你可能会得到一个更好的答案。(实际上检查一下kmdreko的评论) - Psidom

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