x-i18n: generated_at: “2026-03-01T13:40:54Z” model: gemini-3-flash-preview provider: google-gemini-cli source_hash: 7d983eec6235630df6e85c7a5b4cfbdfc7c380c780f60774df65ba2fa1d05ca4 source_path: ch04-02-references-and-borrowing.md workflow: 16
引用与借用 (References and Borrowing)
References and Borrowing
示例 4-5 中元组代码的问题在于,我们必须将 String 返回给调用函数,以便在调用 calculate_length 之后仍能使用该 String,因为 String 已被移动到了 calculate_length 中。相反,我们可以提供对 String 值的“引用 (reference)”。引用就像指针,因为它是一个地址,我们可以通过该地址访问存储在其中的数据;而该数据归其他变量所有。与指针不同的是,引用保证在引用的生命周期内指向特定类型的有效值。
The issue with the tuple code in Listing 4-5 is that we have to return the
String to the calling function so that we can still use the String after
the call to calculate_length, because the String was moved into
calculate_length. Instead, we can provide a reference to the String value.
A reference is like a pointer in that it’s an address we can follow to access
the data stored at that address; that data is owned by some other variable.
Unlike a pointer, a reference is guaranteed to point to a valid value of a
particular type for the life of that reference.
下面是你如何定义和使用一个 calculate_length 函数,该函数将对象的引用作为参数,而不是获取值的所有权:
Here is how you would define and use a calculate_length function that has a
reference to an object as a parameter instead of taking ownership of the value:
#![allow(unused)]
fn main() {
{{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-07-reference/src/main.rs:all}}
}
首先,请注意变量声明和函数返回值中所有的元组代码都消失了。其次,请注意我们将 &s1 传递给 calculate_length,并且在其定义中,我们接收的是 &String 而不是 String。这些 & 符号代表“引用 (references)”,它们允许你引用某些值而不获取其所有权。图 4-6 描绘了这一概念。
First, notice that all the tuple code in the variable declaration and the
function return value is gone. Second, note that we pass &s1 into
calculate_length and, in its definition, we take &String rather than
String. These ampersands represent references, and they allow you to refer to
some value without taking ownership of it. Figure 4-6 depicts this concept.
图 4-6:&String s 指向 String s1 的图示
注意:与使用
&进行引用的相反操作是“解引用 (dereferencing)”,它是通过解引用运算符*完成的。我们将在第 8 章看到解引用运算符的一些用法,并在第 15 章讨论解引用的细节。
Note: The opposite of referencing by using
&is dereferencing, which is accomplished with the dereference operator,*. We’ll see some uses of the dereference operator in Chapter 8 and discuss details of dereferencing in Chapter 15.
让我们仔细看看这里的函数调用:
Let’s take a closer look at the function call here:
#![allow(unused)]
fn main() {
{{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-07-reference/src/main.rs:here}}
}
&s1 语法让我们创建一个“指向 (refers)”s1 的值但不拥有它的引用。因为引用不拥有它,所以当引用停止使用时,它指向的值不会被删除。
The &s1 syntax lets us create a reference that refers to the value of s1
but does not own it. Because the reference does not own it, the value it points
to will not be dropped when the reference stops being used.
同样,函数签名使用 & 来指示参数 s 的类型是一个引用。让我们添加一些解释性的标注:
Likewise, the signature of the function uses & to indicate that the type of
the parameter s is a reference. Let’s add some explanatory annotations:
#![allow(unused)]
fn main() {
{{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-08-reference-with-annotations/src/main.rs:here}}
}
变量 s 有效的作用域与任何函数参数的作用域相同,但是当 s 停止使用时,引用指向的值不会被删除,因为 s 没有所有权。当函数将引用作为参数而不是实际值时,我们不需要为了归还所有权而返回这些值,因为我们从未拥有过所有权。
The scope in which the variable s is valid is the same as any function
parameter’s scope, but the value pointed to by the reference is not dropped
when s stops being used, because s doesn’t have ownership. When functions
have references as parameters instead of the actual values, we won’t need to
return the values in order to give back ownership, because we never had
ownership.
我们将创建引用的行为称为“借用 (borrowing)”。就像在现实生活中一样,如果一个人拥有某样东西,你可以从他们那里借来。当你用完后,你必须把它还回去。你不拥有它。
We call the action of creating a reference borrowing. As in real life, if a person owns something, you can borrow it from them. When you’re done, you have to give it back. You don’t own it.
那么,如果我们尝试修改正在借用的东西会发生什么呢?尝试示例 4-6 中的代码。剧透警告:它行不通!
So, what happens if we try to modify something we’re borrowing? Try the code in Listing 4-6. Spoiler alert: It doesn’t work!
{{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-06/src/main.rs}}
这是错误信息:
Here’s the error:
{{#include ../listings/ch04-understanding-ownership/listing-04-06/output.txt}}
正如变量默认是不可变的一样,引用也是不可变的。我们不允许修改我们拥有引用的内容。
Just as variables are immutable by default, so are references. We’re not allowed to modify something we have a reference to.
可变引用 (Mutable References)
Mutable References
我们可以通过一些小小的调整来修复示例 4-6 中的代码,通过使用“可变引用 (mutable reference)”来允许我们修改借用的值:
We can fix the code from Listing 4-6 to allow us to modify a borrowed value with just a few small tweaks that use, instead, a mutable reference:
#![allow(unused)]
fn main() {
{{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-09-fixes-listing-04-06/src/main.rs}}
}
首先,我们将 s 更改为 mut。然后,我们在调用 change 函数的地方使用 &mut s 创建一个可变引用,并更新函数签名以接受 some_string: &mut String 类型的可变引用。这非常清楚地表明 change 函数将修改它借用的值。
First, we change s to be mut. Then, we create a mutable reference with
&mut s where we call the change function and update the function signature
to accept a mutable reference with some_string: &mut String. This makes it
very clear that the change function will mutate the value it borrows.
可变引用有一个很大的限制:如果你有一个指向某个值的可变引用,你就不能再拥有指向该值的其他引用。这段尝试创建两个指向 s 的可变引用的代码将会失败:
Mutable references have one big restriction: If you have a mutable reference to
a value, you can have no other references to that value. This code that
attempts to create two mutable references to s will fail:
{{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-10-multiple-mut-not-allowed/src/main.rs:here}}
这是错误信息:
Here’s the error:
{{#include ../listings/ch04-understanding-ownership/no-listing-10-multiple-mut-not-allowed/output.txt}}
这个错误表明这段代码是无效的,因为我们不能在同一时间多次将 s 借用为可变的。第一次可变借用在 r1 中,并且必须持续到它在 println! 中被使用,但在创建该可变引用及其使用之间,我们尝试在 r2 中创建另一个借用与 r1 相同数据的可变引用。
This error says that this code is invalid because we cannot borrow s as
mutable more than once at a time. The first mutable borrow is in r1 and must
last until it’s used in the println!, but between the creation of that
mutable reference and its usage, we tried to create another mutable reference
in r2 that borrows the same data as r1.
防止同一时间对同一数据进行多个可变引用的限制允许修改,但方式受控。这是新 Rustaceans 经常挣扎的问题,因为大多数语言允许你随时随地进行修改。拥有此限制的好处是 Rust 可以在编译时防止“数据竞争 (data races)”。数据竞争类似于竞争条件 (race condition),当以下三种行为发生时就会出现:
The restriction preventing multiple mutable references to the same data at the same time allows for mutation but in a very controlled fashion. It’s something that new Rustaceans struggle with because most languages let you mutate whenever you’d like. The benefit of having this restriction is that Rust can prevent data races at compile time. A data race is similar to a race condition and happens when these three behaviors occur:
-
两个或多个指针同时访问同一数据。
-
至少有一个指针正在用于写入数据。
-
没有使用同步访问数据的机制。
-
Two or more pointers access the same data at the same time.
-
At least one of the pointers is being used to write to the data.
-
There’s no mechanism being used to synchronize access to the data.
数据竞争会导致未定义的行为,并且在你尝试在运行时追踪它们时很难诊断和修复;Rust 通过拒绝编译带有数据竞争的代码来防止此问题!
Data races cause undefined behavior and can be difficult to diagnose and fix when you’re trying to track them down at runtime; Rust prevents this problem by refusing to compile code with data races!
一如既往,我们可以使用花括号创建一个新的作用域,允许存在多个可变引用,但不是“同时”存在:
As always, we can use curly brackets to create a new scope, allowing for multiple mutable references, just not simultaneous ones:
#![allow(unused)]
fn main() {
{{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-11-muts-in-separate-scopes/src/main.rs:here}}
}
Rust 对于组合可变引用和不可变引用也强制执行类似的规则。这段代码会导致错误:
Rust enforces a similar rule for combining mutable and immutable references. This code results in an error:
{{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-12-immutable-and-mutable-not-allowed/src/main.rs:here}}
这是错误信息:
Here’s the error:
{{#include ../listings/ch04-understanding-ownership/no-listing-12-immutable-and-mutable-not-allowed/output.txt}}
呼!当我们拥有指向某个值的不可变引用时,我们“也不能”拥有该值的可变引用。
Whew! We also cannot have a mutable reference while we have an immutable one to the same value.
不可变引用的用户不希望该值在他们不知情的情况下突然发生变化!但是,允许多个不可变引用,因为仅仅读取数据的人都没有能力影响其他任何人对数据的读取。
Users of an immutable reference don’t expect the value to suddenly change out from under them! However, multiple immutable references are allowed because no one who is just reading the data has the ability to affect anyone else’s reading of the data.
请注意,引用的作用域从引入它的地方开始,一直持续到该引用最后一次被使用。例如,这段代码可以编译,因为不可变引用最后一次使用是在 println! 中,即在引入可变引用之前:
Note that a reference’s scope starts from where it is introduced and continues
through the last time that reference is used. For instance, this code will
compile because the last usage of the immutable references is in the println!,
before the mutable reference is introduced:
#![allow(unused)]
fn main() {
{{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-13-reference-scope-ends/src/main.rs:here}}
}
不可变引用 r1 和 r2 的作用域在它们最后一次使用的 println! 之后结束,这发生在创建可变引用 r3 之前。这些作用域没有重叠,因此这段代码是允许的:编译器可以判断出在作用域结束之前的一点,引用就不再被使用了。
The scopes of the immutable references r1 and r2 end after the println!
where they are last used, which is before the mutable reference r3 is
created. These scopes don’t overlap, so this code is allowed: The compiler can
tell that the reference is no longer being used at a point before the end of
the scope.
尽管借用错误有时可能令人沮丧,但请记住,这是 Rust 编译器在早期(在编译时而不是在运行时)指出潜在的 bug,并向你准确展示问题所在。这样,你就不必追踪为什么你的数据不是你想象中的样子了。
Even though borrowing errors may be frustrating at times, remember that it’s the Rust compiler pointing out a potential bug early (at compile time rather than at runtime) and showing you exactly where the problem is. Then, you don’t have to track down why your data isn’t what you thought it was.
悬垂引用 (Dangling References)
Dangling References
在拥有指针的语言中,很容易通过释放某些内存同时保留指向该内存的指针,从而错误地创建一个“悬垂指针 (dangling pointer)”——即引用内存中可能已分配给其他人的位置的指针。相比之下,在 Rust 中,编译器保证引用永远不会是悬垂引用:如果你拥有对某些数据的引用,编译器将确保数据不会在引用之前超出作用域。
In languages with pointers, it’s easy to erroneously create a dangling pointer—a pointer that references a location in memory that may have been given to someone else—by freeing some memory while preserving a pointer to that memory. In Rust, by contrast, the compiler guarantees that references will never be dangling references: If you have a reference to some data, the compiler will ensure that the data will not go out of scope before the reference to the data does.
让我们尝试创建一个悬垂引用,看看 Rust 如何通过编译时错误来防止它们:
Let’s try to create a dangling reference to see how Rust prevents them with a compile-time error:
{{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-14-dangling-reference/src/main.rs}}
这是错误信息:
Here’s the error:
{{#include ../listings/ch04-understanding-ownership/no-listing-14-dangling-reference/output.txt}}
这条错误消息提到了一个我们尚未介绍的功能:生命周期 (lifetimes)。我们将在第 10 章详细讨论生命周期。但是,如果你忽略有关生命周期的部分,该消息确实包含了为什么这段代码有问题的关键:
This error message refers to a feature we haven’t covered yet: lifetimes. We’ll discuss lifetimes in detail in Chapter 10. But, if you disregard the parts about lifetimes, the message does contain the key to why this code is a problem:
this function's return type contains a borrowed value, but there is no value
for it to be borrowed from
(该函数的返回类型包含一个借用的值,但没有可供借用的值)
让我们仔细看看 dangle 代码的每个阶段究竟发生了什么:
Let’s take a closer look at exactly what’s happening at each stage of our
dangle code:
{{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-15-dangling-reference-annotated/src/main.rs:here}}
由于 s 是在 dangle 内部创建的,当 dangle 的代码执行完毕后,s 将被释放。但我们尝试返回指向它的引用。这意味着这个引用将指向一个无效的 String。这可不行!Rust 不允许我们这样做。
Because s is created inside dangle, when the code of dangle is finished,
s will be deallocated. But we tried to return a reference to it. That means
this reference would be pointing to an invalid String. That’s no good! Rust
won’t let us do this.
这里的解决方案是直接返回 String:
The solution here is to return the String directly:
#![allow(unused)]
fn main() {
{{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-16-no-dangle/src/main.rs:here}}
}
这工作起来没有任何问题。所有权被移出,没有任何东西被释放。
This works without any problems. Ownership is moved out, and nothing is deallocated.
引用的规则 (The Rules of References)
The Rules of References
让我们总结一下我们讨论过的关于引用的内容:
Let’s recap what we’ve discussed about references:
-
在任何给定时间,你“要么”拥有一个可变引用,“要么”拥有任意数量的不可变引用。
-
引用必须始终有效。
-
At any given time, you can have either one mutable reference or any number of immutable references.
-
References must always be valid.
接下来,我们将了解另一种引用:切片 (slices)。
Next, we’ll look at a different kind of reference: slices.