mirror of
https://github.com/gnu4cn/rust-lang-zh_CN.git
synced 2025-01-13 13:50:37 +08:00
Refining Ch04.
This commit is contained in:
parent
95bd669618
commit
157ea4c20c
@ -1,24 +1,12 @@
|
||||
fn main() {
|
||||
let s = String::from("hello"); // s 进到作用域
|
||||
let s1 = String::from("hello");
|
||||
|
||||
takes_ownership(s); // 变量 s 的值迁移到这个函数中......
|
||||
// ......进而在这里不再有效
|
||||
let length = calculate_length(&s1);
|
||||
|
||||
let x = 5; // x 进到作用域
|
||||
println! ("字符串 '{}' 的长度为:{}", s1, length);
|
||||
}
|
||||
|
||||
makes_copy(x); // x 将迁移到这个函数中,
|
||||
// 但由于 i32 实现了 `Copy` 特质,因此
|
||||
// 后面在使用变量 x 没有问题
|
||||
println! ("x = {x}");
|
||||
} // 到这里,x 超出作用域,接着是 s。但由于 s 的值已被迁移,因此
|
||||
// 不会有特别的事情发生。
|
||||
|
||||
fn takes_ownership(some_string: String) { // some_string 进到作用域
|
||||
println! ("{}", some_string);
|
||||
} // 这里,some_string 超出作用域,而 `drop` 方法会被调用。退回的
|
||||
// 内存被释放。
|
||||
|
||||
fn makes_copy(some_integer: i32) { // some_integer 进到作用域
|
||||
println! ("{}", some_integer);
|
||||
} // 这里,some_integer 超出作用域。没有特别事情发生。
|
||||
fn calculate_length(s: &String) -> usize {
|
||||
s.len()
|
||||
}
|
||||
|
||||
|
@ -460,9 +460,9 @@ Rust 确实允许咱们使用元组返回多个值,如下清单 4-5 中所示
|
||||
fn main() {
|
||||
let s1 = String::from("hello");
|
||||
|
||||
let (s2, len): (String, usize) = calculate_length(s1);
|
||||
let (s2, len) = calculate_length(s1);
|
||||
|
||||
println! ("字符串 {} 的长度为:{}", s2, len);
|
||||
println! ("字符串 '{}' 的长度为:{}", s2, len);
|
||||
}
|
||||
|
||||
fn calculate_length(s: String) -> (String, usize) {
|
||||
@ -472,6 +472,6 @@ fn calculate_length(s: String) -> (String, usize) {
|
||||
}
|
||||
```
|
||||
|
||||
*清单 4-5:返回参数所有权*
|
||||
*清单 4-5:返回参数的所有权*
|
||||
|
||||
这虽然间接实现了消除变量所有权占据下,函数的使用变量,但对于这种本应常见的概念来说,这样做就过于花哨,且带来了大量工作负担。幸运的是,Rust 有着一项使用某个值而不转移所有权,名为 *引用(references)* 的特性。
|
||||
但是,对于一个本应很常见的概念来说,这样做太过仪式化和工作量都太大了。幸运的是,Rust 有一种使用某个值,而不转移所有权的特性,叫做 *引用,references*。
|
||||
|
@ -3,9 +3,10 @@
|
||||
**References and Borrowing**
|
||||
|
||||
|
||||
清单 4-5 中那些元组代码的问题,是因为那个 `String` 值已被迁移到 `calculate_length` 函数中,因此那里就必须将那个 `String` 值,返回给调用函数(the calling funciton, 即清单 4-5 中的 `main` 函数),进而在对 `calculate_length` 的调用之后,仍然可以使用那个 `String` 的堆上数据。相反,咱们可以提供到那个 `String` 值的引用。所谓 *引用,reference*,与指针相似的是,在引用中的是个地址,咱们循着这个地址,就可以访问保存在那个地址处的数据,而这个数据则是为某个别的变量所拥有的。与指针不同的是,在引用存活期间,其保证是指向了特定类型有效值的。
|
||||
清单 4-5 中,元组代码的问题在于,我们必须将那个 `String` 返回给调用函数,以便咱们在调用 `calculate_length` 后,仍能使用这个 `String`,因为这个 `String` 已被迁移到 `calculate_length` 中。相反,我们可以提供一个到该 `String` 值的引用。*引用,reference* 与指针类似,其是个我们可以沿着他,访问存储在其中数据的地址;其中的数据为其他变量所有。与指针不同的是,可以保证某个引用在其生命周期内,始终指向某个特定类型的有效值。
|
||||
|
||||
下面是如何定义和使用,以对象引用作为参数,而非取得值所有权的 `calculate_length` 函数:
|
||||
|
||||
以下是应如何定义和使用,将某个对象的引用作为参数,而非占用该值所有权的方式下的 `calculate_length` 函数:
|
||||
|
||||
文件名:`src/main.rs`
|
||||
|
||||
@ -15,7 +16,7 @@ fn main() {
|
||||
|
||||
let length = calculate_length(&s1);
|
||||
|
||||
println! ("字符串 {} 的长度为:{}", s1, length);
|
||||
println! ("字符串 '{}' 的长度为:{}", s1, length);
|
||||
}
|
||||
|
||||
fn calculate_length(s: &String) -> usize {
|
||||
@ -23,33 +24,40 @@ fn calculate_length(s: &String) -> usize {
|
||||
}
|
||||
```
|
||||
|
||||
首先,注意到变量声明与函数返回值中的全部元组代码都不见了。其次,留意到这里是将 `&s1` 传入到 `calculate_length` 中的,同时在该函数的定义中,采用的是 `&String` 而非 `String`。这些 `&` 符号,these ampersands,表示了 *引用,references*,他们实现了在无需占用某个值所有权的情况下,引用到该值。下图 4-5 对此概念进行了描述。
|
||||
首先,请注意变量声明和函数返回值中的所有元组代码都不见了。其次,请注意我们将 `&s1` 传入到 `calculate_length`,且在其定义中,我们使用了 `&String` 而不是 `String`。这些 `&` 符合表示了 *引用*,而他们允许咱们,在取得某个值所有权的情况下,对其进行引用。下图 4-5 描述了这一概念。
|
||||
|
||||
|
||||
![指向 `String s1` 的 `&String s` 图示](images/Ch04_05.svg)
|
||||
|
||||
*图 4-5:指向 `String s1` 的 `&String s` 图示*
|
||||
|
||||
|
||||
> 注意:这种经由使用 `&` (取地址)运算符,而得到的变量引用的反面,即为 *解引用,dereferencing*,解引用是以解引用运算符 `*` 达成的。在第 8 章中就会看到这个 [解引用运算符的使用](Ch08_Common_Collections.md#对矢量中那些值的迭代),而在第 15 章中,则会对解引用的细节加以讨论。
|
||||
> **注意**:与使用 `&` 进行引用相反的是,*解引用,dereferencing*,他是通过解引用操作符 `*` 实现的。我们将在第 8 章,看到解引用操作符的一些用法,并在第 15 章讨论解引用的细节。
|
||||
|
||||
|
||||
我们来仔细看看这里的函数调用:
|
||||
|
||||
来细看一下这里的函数调用:
|
||||
|
||||
```rust
|
||||
let s1 = String::from("hello");
|
||||
let len = calculate_length(&s1);
|
||||
let s1 = String::from("hello");
|
||||
|
||||
let len = calculate_length(&s1);
|
||||
```
|
||||
|
||||
这种 `&s1` 语法,实现了创建出一个 *指向,refers* 到 `s1` 的值,却不占有那个值的引用变量。由于引用不占有那个值,因此在引用停止使用(超出作用域)时,其所指向值就不会被弃用。
|
||||
|
||||
与此类似,那个函数签名同样使用 `&` 运算符,来表明参数 `s` 的类型是个引用。下面就来添加一些说明性的注解:
|
||||
通过 `&s1` 这种语法,我们可以创建出一个指向 `s1` 值,但不拥有他的引用。由于这个引用不拥有 `s1`,因此其停止使用时,他所指向的值,不会被丢弃。
|
||||
|
||||
同样,那个函数的签名,使用了 `&` 来表明参数 `s` 的类型是个引用。我们来添加一些解释性注释:
|
||||
|
||||
|
||||
```rust
|
||||
fn calculate_length(s: &String) -> usize { // 变量 s 为到某个 String 值的引用
|
||||
fn calculate_length(s: &String) -> usize { // s 是个到某 String 的引用
|
||||
s.len()
|
||||
} // 到这里,变量 s 超出作用域。但由于他并没有他指向值的所有权,因此什么
|
||||
// 也不会发生。
|
||||
} // 这里,s 会超出作用域。但由于他没有其指向值的所有权,因此该
|
||||
// 值不会被丢弃。
|
||||
```
|
||||
|
||||
|
||||
变量 `s` 于其间有效的那个作用域,与所有函数参数作用域是相同的,而由于变量 `s` 不拥有经引用而指向的那个值的所有权,因此在变量 `s` 停止被使用时,那个所指向的值就不会被丢弃。在函数以引用变量,而非真实值作为参数时,由于根本就没有拥有过所有权,那么就不再需要为了交回所有权,而将那些值返回了。
|
||||
|
||||
咱们把这种创建出引用的行为,叫做 *借用,borrowing*。正如日常生活中,当某人拥有某个物件时,咱们就可以把这个物件从那个人那里借用一下。在使用完毕后,咱们必须将其还回。咱们是不拥有该物件的。
|
||||
|
Loading…
Reference in New Issue
Block a user