Rust - 借用
Rust - 借用与引用
Section titled “Rust - 借用与引用”在所有权(Ownership)一章中,我们了解到当一个值被分配给另一个变量或传递给一个函数时,它的所有权会被 移动(moved)。移动之后,原所有者不能再使用该值。这确保了内存安全,但也可能带来不便。不断地来回传递所有权会很麻烦。
这就是借用(borrowing)的作用。借用允许你创建一个值的引用(reference),从而在不取得所有权的情况下访问它。
问题:所有权转移
Section titled “问题:所有权转移”fn calculate_length(s: String) -> (String, usize) { let length = s.len(); (s, length) // Return ownership of the String and the length}
fn main() { let s1 = String::from("hello");
// We pass s1, and also get it back. let (s2, len) = calculate_length(s1);
println!("The length of '{}' is {}.", s2, len);}这段代码可以工作,但它很笨拙。我们不得不从函数中返回 String,以便能够继续使用它。这正是借用的完美用例。
解决方案:使用引用进行借用
Section titled “解决方案:使用引用进行借用”我们可以传递值的引用(&String),而不是直接传递值本身(String)。引用就像一个指针,它保证指向特定类型的有效值。& 符号是引用运算符。
// The function now takes a reference to a String.fn calculate_length(s: &String) -> usize { s.len()} // `s` goes out of scope here, but because it does not own what it refers to, // the String is not dropped.
fn main() { let s1 = String::from("hello");
// We pass a reference to s1. Ownership is not moved. let len = calculate_length(&s1);
println!("The length of '{}' is {}.", s1, len); // s1 is still valid here!}The length of 'hello' is 5.两种类型的引用
Section titled “两种类型的引用”正如变量默认是不可变的,引用也是如此。有两种类型的引用:
- 不可变引用(Immutable Reference)(
&T):允许你读取数据但不能修改它。 - 可变引用(Mutable Reference)(
&mut T):允许你同时读取和修改数据。
示例:可变引用
Section titled “示例:可变引用”要创建可变引用,你使用 &mut。原始变量也必须声明为 mut。* 运算符用于**解引用(dereference)**引用,允许你访问它指向的值。
fn add_suffix(s: &mut String) { s.push_str(" World"); // Methods can be called directly on the reference}
fn increment(num: &mut i32) { *num += 1; // For primitive types, we must dereference with *}
fn main() { let mut my_string = String::from("Hello"); println!("Before: {}", my_string); add_suffix(&mut my_string); println!("After: {}", my_string);
let mut count = 5; println!("\nBefore: {}", count); increment(&mut count); println!("After: {}", count);}Before: HelloAfter: Hello World
Before: 5After: 6Rust 编译器强制执行一套关键规则,以在编译时防止数据竞争(data races)和其他并发错误。这些规则适用于给定作用域内的引用:
- 规则 1:你可以拥有一个可变引用,或者任意数量的不可变引用。
- 规则 2:引用必须始终有效(即,它们不能是悬垂引用(dangling references),指向已释放的内存)。
这意味着当不可变引用存在时,你不能拥有一个可变引用。所有不可变读者都必须消失,然后才能获得一个可变写入器。
示例:违反规则
Section titled “示例:违反规则”以下代码将无法编译,因为它违反了规则 1。
// This code will fail to compile!fn main() { let mut s = String::from("hello");
let r1 = &s; // an immutable borrow starts here let r2 = &s; // another immutable borrow is fine
// COMPILE ERROR: cannot borrow `s` as mutable because it is also borrowed as immutable let r3 = &mut s;
// The compiler knows that r1 and r2 could be used after the mutable borrow, which is unsafe. println!("Immutable borrows: {}, {}", r1, r2);}编译器的错误消息在这里非常有帮助。它防止了数据在被读取时意外更改的情况,这很不安全。
- 优先选择不可变借用:当你只需要读取数据时,使用
&T。这更安全,并允许更灵活的代码。 - 保持可变借用简短:可变借用的作用域应尽可能小,以允许代码的其他部分访问数据。
- 对集合使用切片:编写操作数组或向量的函数时,接受切片(
&[T]或&mut [T]),而不是直接引用集合(&Vec<T>)。这使你的函数更通用和可重用。