为什么这不算作不可变借用?

3

我正在阅读Rust官方手册并查看第4.3节中的清单4-8。

代码如下:

fn first_word(s: &String) -> usize {
    let bytes = s.as_bytes();

    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return i;
        }
    }

    s.len()
}

fn main() {
    let mut s = String::from("hello world");

    let word = first_word(&s);

    s.clear();
}

这一行:

let word = first_word(&s);

似乎借用了对 s 的不可变引用。(这就是我猜错的地方,我只是不知道为什么。)

在下一行中,我们通过调用 clear() 方法来改变 s

我原本期望编译器会报错:

cannot borrow `s` as mutable because it is also borrowed as immutable

为什么这个可以编译?
1个回答

5
在调用 first_word 函数期间,字符串 s 被不可变地借用。一旦控制权在调用 first_word 后返回到 main,该字符串将不再被视为已借用,因此可以像您观察到的那样进行改变。
如果first_word返回一个&String,你通过将其赋值给一个变量来扩展其生命周期,那么你会看到预期的错误。例如:
fn first_word(s: &String) -> &String {
    &s
}

fn main() {
    let mut s = String::from("hello world");
    let word = first_word(&s);
    s.clear();
}

无法将 s 借用为可变的,因为它也被借用为不可变的。

https://rust.godbolt.org/z/cMVdVf

在这种情况下,添加一个额外的作用域可以解决这个问题:
fn main() {
    let mut s = String::from("hello world");

    {
        let word = first_word(&s);
    }

    s.clear();
}

但是在这种情况下,为什么当我们返回一个切片而不是一个整数时,它会编译失败呢?(请参见此页面上的红色列表,滚动到大约75%的位置。) - Darshak Parikh
1
@DarshakParikh:这与我向您展示的返回-> String&代码原理相同。借用在“first_word”作用域之外仍然存在,这意味着我们返回到“main”后,“s”仍然被借用。 - Vittorio Romeo
我明白了,谢谢你的解释。但是为什么一旦借用函数返回后它还能存在呢?返回引用与返回基本类型有什么特别之处? - Darshak Parikh
1
没关系,我懂了。这是因为我们将返回的引用分配给一个变量,这使得该变量成为新的借用者。 - Darshak Parikh

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