在 Rust 中,究竟什么是可变借用和不可变借用?

In Rust, what exactly are mutable and immutable borrows?

我坚持使用 借用可变 的 Rust 概念:

#[derive(Debug)]
struct Rectangle {
    height: u32,
    width: u32,
}

fn mut_area(rect_mut: &mut Rectangle) -> u32 {
    rect_mut.width /= 2;
    rect_mut.height * rect_mut.width
}

fn mut_string(s: &mut String) -> &str {
    s.push_str("!");
    let len = s.len();
    &s[0..len / 2]
}

fn main() {
    let mut rect = Rectangle {
        height: 50,
        width: 40,
    };
    println!("original rect: {:?}", rect);
    let a = mut_area(&mut rect);
    println!("area of rect: {}", a);
    println!("now rect: {:?}", rect);

    let mut s = String::from("helloworld");
    println!("original s: {}", s);
    let half = mut_string(&mut s);
    println!("half of the modified string: {}", half);
    println!("modified s: {}", s);
}

当我尝试编译它时,编译器告诉我:

error[E0502]: cannot borrow `s` as immutable because it is also borrowed as mutable
  --> <anon>:32:32
   |
30 |     let half = mut_string(&mut s);
   |                                - mutable borrow occurs here
31 |     println!("half of the modified string: {}", half);
32 |     println!("modified s: {}", s);
   |                                ^ immutable borrow occurs here
33 | }
   | - mutable borrow ends here

我知道有一条关于可变引用的规则:

you can only have one mutable reference to a particular piece of data in a particular scope.

但是为什么rect可以借而s不能借呢?我如何在这里实现我想要的 - 在函数调用后打印修改后的字符串?

在调用带有对 rect 的可变引用的函数后可以打印 a 的原因是它 returns 一个 u32Copyable - 无需限制 rect 的进一步使用,因为在调用 mut_area 后不再借用它。

你的 mut_string,另一方面,returns 是对其参数的引用,所以只要 half 在范围内,可变借用就一直有效。这就是为什么你不能为了 println!().

的目的而一成不变地借用 s

为了实现你想要的效果,我会在 mut_string 函数之外改变 s(现在稍微不同的名字是个好主意)所以没有有效的可变借用- 它的参数可以不可变地借用:

fn mut_string(s: &str) -> &str {
    let len = s.len();
    &s[0..len / 2]
}

fn main() {
    let mut rect = Rectangle {
        height: 50,
        width: 40,
    };
    println!("original rect: {:?}", rect);
    let a = mut_area(&mut rect); // rect's mutable borrow expires after this assignment
    println!("area of rect: {}", a);
    println!("now rect: {:?}", rect);

    let mut s = String::from("helloworld");
    println!("original s: {}", s);
    s.push_str("!"); // s is mutated here
    let half = mut_string(&s); // s is borrowed immutably
    println!("half of the modified string: {}", half);
    println!("modified s: {}", s); // another immutable borrow of s
}