为什么我在嵌套函数中可以拥有多个可变引用(&mut)?(Rust)

3
我刚接触 Rust,想知道为什么以下代码不会出现“不能同时将`val`作为可变引用借用多次”的错误。在到达`second_layer`函数时,我应该有三个对同一原始的`val`变量的引用:

主函数体中的`val_ref`

第一个层级函数体中的`val_ref2`

第二个层级函数体中的`val_ref3`

感谢任何帮助!

fn first_layer(val_ref2: &mut String)
{
    *val_ref2 = String::from("first_layer");
    println!("{}", val_ref2);
    second_layer(val_ref2);
}

fn second_layer(val_ref3: &mut String)
{
    *val_ref3 = String::from("second_layer");
    println!("{}", val_ref3);

}

fn main()
{
    let mut val = String::from("asdf");
    let val_ref: &mut String = &mut val;

    first_layer(val_ref);

    println!("{}", val_ref);

}

感谢您的信件。

3
这是由于隐式再借用。请查看此帖子 - Joe_Jingyu
2
@Joe_Jingyu,允许嵌套引用存在的不是重新借用,而是相反 - 是嵌套引用允许重新借用(显式或隐式)。这个问题似乎是在问为什么允许嵌套引用的别名,而链接的答案并没有解决这个问题。 - user4815162342
1
感谢您的评论,@user4815162342。我不清楚您为什么认为链接的帖子没有解决这个问题。难道不是因为重新借用导致val-refmain中调用first_layer后没有被移动并且仍然可以访问吗? - Joe_Jingyu
1
@Joe_Jingyu 因为这里的提问者想知道嵌套引用是如何“开始”的,而不管是否存在隐式重新借用。换句话说,为什么 let mut i = 0i32; let r1 = &mut i; let r2 = &mut *r1 能够编译通过,尽管它显然创建了对 i 的别名可变引用?隐式重新借用的讨论并没有涉及到这一点,因为它只是解释了如何通过创建嵌套引用来防止引用被移动。它没有解释为什么允许嵌套内部引用别名外部数据。 - user4815162342
1
@user4815162342 我理解你的观点。然而,我不确定RFC#2094是否是关于reborrow动机的好文档。如果你知道更适合初学者的文档,我也很想阅读。谢谢。 - Joe_Jingyu
1
很抱歉,除了你已经知道的SO答案之外,我没有其他的建议。改进reborrowing文档是这个问题的目标。 - user4815162342
1个回答

0

一个函数调用另一个函数时,参考参数在函数调用期间暂时不存在。

标识符不能自动延续到更深的函数调用堆栈中。仅因为一个函数调用了另一个函数,并不意味着被调用者可以访问调用者的本地变量。如果你尝试这样做,会得到一个not found in this scope的错误。

你可以(尝试)创建一个闭包捕获调用者的环境,然后重复使用其中一个调用者的变量。但是,如果你违反了借用规则,你会发现编译器会有所抱怨。

fn first_layer(val_ref2: &mut String) {
    // println!("{}", val); // Cannot use val from main!
    *val_ref2 = String::from("first_layer");
    println!("{}", val_ref2);
    (|val_ref3: &mut String| {
        *val_ref3 = String::from("second_layer");
        println!("{}", val_ref3);
        println!("{}", val_ref2); // This is not allowed
    })(val_ref2);
}

fn main() {
    let mut val = String::from("asdf");
    let val_ref: &mut String = &mut val;

    first_layer(val_ref);

    println!("{}", val_ref);
}

另一种思考方式是使用内联。如果你将函数second_layer内联到first_layer中,你会得到:
fn first_layer(val_ref2: &mut String)
{
    *val_ref2 = String::from("first_layer");
    println!("{}", val_ref2);
    *val_ref2 = String::from("second_layer");
    println!("{}", val_ref2);
}

这完全没问题!

那么,将最后两行代码抽象成自己的函数也应该完全没问题。


请注意,您还可以在同一个函数中创建对同一数据的两个可变引用,例如 let mut i = 0i32; let r1 = &mut i; let r2 = &mut *r1; - user4815162342
@user4815162342,如果你尝试使用它们,你将会得到一个错误:fn main() { let mut i = 0; let r1 = &mut i; let r2 = &mut *r1; *r1 += 1; *r2 += 1; } - hkBst
如果您使用r2,并且仅在r2被删除后使用r1,则不会出现错误。但这并不否定Rust首先允许它们存在的问题。我相信原因是:1)允许重新借用,2)允许投影,即获得对所引用数据的一部分的可变引用。嵌套引用然后是投影的特殊情况。 - user4815162342

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