关于 Rust 函数参数和所有权的一点问题

5

以下是我的问题:

fn main() {
    let mut s = String::from("hello");
    let s1 = &mut s;
    let s2 = s1;

    *s2 = String::from("world1");
    *s1 = String::from("world2");

    println!("{:?}", s);
}

如果将代码更改如下:

当使用类型为&mut String的变量s1时,会导致编译错误,因为它没有实现Copy trait。

fn c(s: &mut String) -> &mut String {
    s
}

fn main() {
    let mut s = String::from("hello");
    let s1 = &mut s;
    let s2 = c(s1);

    *s2 = String::from("world1");
    *s1 = String::from("world2");

    println!("{:?}", s);
}

这段代码将会编译且没有任何错误信息。

当一个引用被传递给函数时,意味着该引用借用了该值,而不是拥有它。

但在上面的情况中,当s1被传递给 fn c 并立即返回时,s2 借用了s1,因此在s2生命周期内,s1无法进行解除引用。

那么当s1传递到 fn c 中发生了什么呢?


不必使用 let s2 =c(s1);,你可以使用 let s2 = &mut *s1;:引用并非 Copy,但你可以像函数中所做的那样获取新引用。请注意,这不是一个答案:我对导致 c 中解引用规则不够清楚。 - Denys Séguret
看起来你有两个可变引用指向同一个字符串,但是如果在 *s1 = String::from("world2"); 之后添加 println!("{:?}", s2);,你会得到 cannot assign to *s1 because it is borrowed。所以我猜这可能与 NLL 有关。不过这是一个非常有趣的问题。 - Svetlin Zarev
如果您在没有 fn c 的情况下重新借用 s1,情况也是一样的:let s2 = &mut *s1;。所以我猜,在 fn c() 的情况下,rustc 在幕后执行了一个重新借用,而在 s2 = s1 的情况下,它只是进行了一个 move。相关问题:https://dev59.com/9lgQ5IYBdhLWcg3wRBzK - Svetlin Zarev
这绝对是许多新手在Rust中遇到的绊脚石。幸运的是,我认为Sven对重复问题的回答是一个非常好的解释,说明了为什么会出现这种情况。 - trent
2
从那个答案中,另一种使函数编译的方法是显式地给s2一个引用类型:let s2: &mut _ = s1;将会正常工作,因为编译器会推断出在目标被知道是可变引用时重新借用。 - trent
这对于Rust学习者来说是一个绊脚石并不奇怪,因为这种行为既违反直觉又没有文档记录。在我看来,引入可变引用的隐式重新借用是一个错误。 - Sven Marnach
1个回答

1

根据 @Denys Séguret 提供的提示,我猜当 s1 传递给 fn C 时,Rust core 将参数 s1 编译成类似于 &mut *s1 的东西,因此对 s1 进行了不可变借用。

这就是为什么如果我们放置

*s2 = String::from("world1");

behind

*s1 = String::from("world2");

"Rust会告诉我们:"
assignment to borrowed `*s1`

当s2超出其生命周期范围时,就不再借用s1了,因此s1可以再次被解引用。但我不确定这是否是正确的解释。

这似乎是一个特殊情况,因为如果您使用身份函数而不是 c,则它无法正常工作。 - Masklinn
什么是恒等函数? - Jeff Garrett
不用在意...... - Jeff Garrett

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