diff --git a/projects/ownership_demo/src/main.rs b/projects/ownership_demo/src/main.rs index e45255b..be788be 100644 --- a/projects/ownership_demo/src/main.rs +++ b/projects/ownership_demo/src/main.rs @@ -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() +} diff --git a/src/ownership/about_ownership.md b/src/ownership/about_ownership.md index 98e4b3a..00c605b 100644 --- a/src/ownership/about_ownership.md +++ b/src/ownership/about_ownership.md @@ -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*。 diff --git a/src/ownership/references_and_borrowing.md b/src/ownership/references_and_borrowing.md index a926720..f96ff5e 100644 --- a/src/ownership/references_and_borrowing.md +++ b/src/ownership/references_and_borrowing.md @@ -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*。正如日常生活中,当某人拥有某个物件时,咱们就可以把这个物件从那个人那里借用一下。在使用完毕后,咱们必须将其还回。咱们是不拥有该物件的。