Update codes

This commit is contained in:
Aaran Xu 2022-01-27 01:28:47 +08:00
parent 91d8e8d41a
commit 2984c8139e
50 changed files with 136 additions and 479 deletions

View File

@ -1,7 +1,6 @@
[package]
name = "ownership"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
[dependencies]

View File

@ -1,9 +1,9 @@
fn main() {
// ANCHOR: here
{ // s is not valid here, its not yet declared
let s = "hello"; // s is valid from this point forward
{ // s 在这里无效, 它尚未声明
let s = "hello"; // 从此处起s 开始有效
// do stuff with s
} // this scope is now over, and s is no longer valid
// 使用 s
} // 此作用域已结束s 不再有效
// ANCHOR_END: here
}
}

View File

@ -1,7 +1,6 @@
[package]
name = "ownership"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
[dependencies]

View File

@ -1,7 +1,6 @@
[package]
name = "ownership"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
[dependencies]

View File

@ -1,23 +1,21 @@
fn main() {
let s = String::from("hello"); // s comes into scope
let s = String::from("hello"); // s 进入作用域
takes_ownership(s); // s's value moves into the function...
// ... and so is no longer valid here
takes_ownership(s); // s 的值移动到函数里 ...
// ... 所以到这里不再有效
let x = 5; // x comes into scope
let x = 5; // x 进入作用域
makes_copy(x); // x would move into the function,
// but i32 is Copy, so it's okay to still
// use x afterward
makes_copy(x); // x 应该移动函数里,
// 但 i32 是 Copy 的,所以在后面可继续使用 x
} // Here, x goes out of scope, then s. But because s's value was moved, nothing
// special happens.
} // 这里, x 先移出了作用域,然后是 s。但因为 s 的值已被移走,
// 所以不会有特殊操作
fn takes_ownership(some_string: String) { // some_string comes into scope
println!("{}", some_string);
} // Here, some_string goes out of scope and `drop` is called. The backing
// memory is freed.
fn takes_ownership(some_string: String) { // some_string 进入作用域
println!("{}", some_string);
} // 这里some_string 移出作用域并调用 `drop` 方法。占用的内存被释放
fn makes_copy(some_integer: i32) { // some_integer comes into scope
println!("{}", some_integer);
} // Here, some_integer goes out of scope. Nothing special happens.
fn makes_copy(some_integer: i32) { // some_integer 进入作用域
println!("{}", some_integer);
} // 这里some_integer 移出作用域。不会有特殊操作

View File

@ -1,7 +1,6 @@
[package]
name = "ownership"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
[dependencies]

View File

@ -1,29 +1,25 @@
fn main() {
let s1 = gives_ownership(); // gives_ownership moves its return
// value into s1
let s1 = gives_ownership(); // gives_ownership 将返回值
// 移给 s1
let s2 = String::from("hello"); // s2 comes into scope
let s2 = String::from("hello"); // s2 进入作用域
let s3 = takes_and_gives_back(s2); // s2 is moved into
// takes_and_gives_back, which also
// moves its return value into s3
} // Here, s3 goes out of scope and is dropped. s2 goes out of scope but was
// moved, so nothing happens. s1 goes out of scope and is dropped.
let s3 = takes_and_gives_back(s2); // s2 被移动到
// takes_and_gives_back 中,
// 它也将返回值移给 s3
} // 这里, s3 移出作用域并被丢弃。s2 也移出作用域,但已被移走,
// 所以什么也不会发生。s1 移出作用域并被丢弃
fn gives_ownership() -> String { // gives_ownership will move its
// return value into the function
// that calls it
fn gives_ownership() -> String { // gives_ownership 将返回值移动给
// 调用它的函数
let some_string = String::from("hello"); // some_string comes into scope
let some_string = String::from("yours"); // some_string 进入作用域
some_string // some_string is returned and
// moves out to the calling
// function
some_string // 返回 some_string 并移出给调用的函数
}
// takes_and_gives_back will take a String and return one
fn takes_and_gives_back(a_string: String) -> String { // a_string comes into
// scope
// takes_and_gives_back 将传入字符串并返回该值
fn takes_and_gives_back(a_string: String) -> String { // a_string 进入作用域
a_string // a_string is returned and moves out to the calling function
a_string // 返回 a_string 并移出给调用的函数
}

View File

@ -1,7 +1,6 @@
[package]
name = "ownership"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
[dependencies]

View File

@ -7,7 +7,7 @@ fn main() {
}
fn calculate_length(s: String) -> (String, usize) {
let length = s.len(); // len() returns the length of a String
let length = s.len(); // len() 返回字符串的长度
(s, length)
}

View File

@ -1,7 +1,6 @@
[package]
name = "ownership"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
[dependencies]

View File

@ -8,9 +8,5 @@ error[E0596]: cannot borrow `*some_string` as mutable, as it is behind a `&` ref
8 | some_string.push_str(", world");
| ^^^^^^^^^^^ `some_string` is a `&` reference, so the data it refers to cannot be borrowed as mutable
error: aborting due to previous error
For more information about this error, try `rustc --explain E0596`.
error: could not compile `ownership`
To learn more, run the command again with --verbose.
error: could not compile `ownership` due to previous error

View File

@ -1,7 +1,6 @@
[package]
name = "ownership"
version = "0.1.0"
authors = ["Carol (Nichols || Goulding) <carol.nichols@gmail.com>"]
edition = "2018"
[dependencies]

View File

@ -1,7 +1,6 @@
[package]
name = "ownership"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
[dependencies]

View File

@ -14,11 +14,11 @@ fn first_word(s: &String) -> usize {
fn main() {
let mut s = String::from("hello world");
let word = first_word(&s); // word will get the value 5
let word = first_word(&s); // word 的值为 5
s.clear(); // this empties the String, making it equal to ""
s.clear(); // 这清空了字符串,使其等于 ""
// word still has the value 5 here, but there's no more string that
// we could meaningfully use the value 5 with. word is now totally invalid!
// word 在此处的值仍然是 5
// 但是没有更多的字符串让我们可以有效地应用数值 5。word 的值现在完全无效!
}
// ANCHOR_END: here

View File

@ -1,7 +1,6 @@
[package]
name = "ownership"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
[dependencies]

View File

@ -16,16 +16,21 @@ fn first_word(s: &str) -> &str {
fn main() {
let my_string = String::from("hello world");
// first_word works on slices of `String`s
// `first_word` 接受 `String` 的切片,无论是部分还是全部
let word = first_word(&my_string[0..6]);
let word = first_word(&my_string[..]);
// `first_word` 也接受 `String` 的引用,
// 这等同于 `String` 的全部切片
let word = first_word(&my_string);
let my_string_literal = "hello world";
// first_word works on slices of string literals
// `first_word` 接受字符串字面量的切片,无论是部分还是全部
let word = first_word(&my_string_literal[0..6]);
let word = first_word(&my_string_literal[..]);
// Because string literals *are* string slices already,
// this works too, without the slice syntax!
// 因为字符串字面值**就是**字符串 slice
// 这样写也可以,即不使用 slice 语法!
let word = first_word(my_string_literal);
}
// ANCHOR_END: usage

View File

@ -1,7 +1,6 @@
[package]
name = "ownership"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
[dependencies]

View File

@ -2,8 +2,8 @@ fn main() {
// ANCHOR: here
let mut s = String::from("hello");
s.push_str(", world!"); // push_str() appends a literal to a String
s.push_str(", world!"); // push_str() 在字符串后追加字面值
println!("{}", s); // This will print `hello, world!`
println!("{}", s); // 将打印 `hello, world!`
// ANCHOR_END: here
}

View File

@ -1,7 +1,6 @@
[package]
name = "ownership"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
[dependencies]

View File

@ -1,10 +1,10 @@
fn main() {
// ANCHOR: here
{
let s = String::from("hello"); // s is valid from this point forward
let s = String::from("hello"); // 从此处起s 开始有效
// do stuff with s
} // this scope is now over, and s is no
// longer valid
// 使用 s
} // 此作用域已结束,
// s 不再有效
// ANCHOR_END: here
}

View File

@ -1,7 +1,6 @@
[package]
name = "ownership"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
[dependencies]

View File

@ -1,7 +1,6 @@
[package]
name = "ownership"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
[dependencies]

View File

@ -11,9 +11,5 @@ error[E0382]: borrow of moved value: `s1`
5 | println!("{}, world!", s1);
| ^^ value borrowed here after move
error: aborting due to previous error
For more information about this error, try `rustc --explain E0382`.
error: could not compile `ownership`
To learn more, run the command again with --verbose.
error: could not compile `ownership` due to previous error

View File

@ -1,7 +1,6 @@
[package]
name = "ownership"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
[dependencies]

View File

@ -1,7 +1,6 @@
[package]
name = "ownership"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
[dependencies]

View File

@ -1,7 +1,6 @@
[package]
name = "ownership"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
[dependencies]

View File

@ -1,7 +1,6 @@
[package]
name = "ownership"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
[dependencies]

View File

@ -9,6 +9,6 @@ fn main() {
// ANCHOR: here
fn calculate_length(s: &String) -> usize { // s is a reference to a String
s.len()
} // Here, s goes out of scope. But because it does not have ownership of what
// it refers to, nothing happens.
} // 这里s 离开了作用域。但因为它并不拥有引用值的所有权,
// 所以什么也不会发生
// ANCHOR_END: here

View File

@ -1,7 +1,6 @@
[package]
name = "ownership"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
[dependencies]

View File

@ -1,7 +1,6 @@
[package]
name = "ownership"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
[dependencies]

View File

@ -11,9 +11,5 @@ error[E0499]: cannot borrow `s` as mutable more than once at a time
7 | println!("{}, {}", r1, r2);
| -- first borrow later used here
error: aborting due to previous error
For more information about this error, try `rustc --explain E0499`.
error: could not compile `ownership`
To learn more, run the command again with --verbose.
error: could not compile `ownership` due to previous error

View File

@ -1,7 +1,6 @@
[package]
name = "ownership"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
[dependencies]

View File

@ -4,7 +4,7 @@ fn main() {
{
let r1 = &mut s;
} // r1 goes out of scope here, so we can make a new reference with no problems.
} // r1 在这里离开了作用域,所以我们完全可以创建一个新的引用
let r2 = &mut s;
// ANCHOR_END: here

View File

@ -1,7 +1,6 @@
[package]
name = "ownership"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
[dependencies]

View File

@ -12,9 +12,5 @@ error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immuta
8 | println!("{}, {}, and {}", r1, r2, r3);
| -- immutable borrow later used here
error: aborting due to previous error
For more information about this error, try `rustc --explain E0502`.
error: could not compile `ownership`
To learn more, run the command again with --verbose.
error: could not compile `ownership` due to previous error

View File

@ -2,9 +2,9 @@ fn main() {
// ANCHOR: here
let mut s = String::from("hello");
let r1 = &s; // no problem
let r2 = &s; // no problem
let r3 = &mut s; // BIG PROBLEM
let r1 = &s; // 没问题
let r2 = &s; // 没问题
let r3 = &mut s; // 大问题
println!("{}, {}, and {}", r1, r2, r3);
// ANCHOR_END: here

View File

@ -1,7 +1,6 @@
[package]
name = "ownership"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
[dependencies]

View File

@ -2,12 +2,12 @@ fn main() {
// ANCHOR: here
let mut s = String::from("hello");
let r1 = &s; // no problem
let r2 = &s; // no problem
let r1 = &s; // 没问题
let r2 = &s; // 没问题
println!("{} and {}", r1, r2);
// r1 and r2 are no longer used after this point
// 此位置之后 r1 和 r2 不再使用
let r3 = &mut s; // no problem
let r3 = &mut s; // 没问题
println!("{}", r3);
// ANCHOR_END: here
}

View File

@ -1,7 +1,6 @@
[package]
name = "ownership"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
[dependencies]

View File

@ -12,9 +12,5 @@ help: consider using the `'static` lifetime
5 | fn dangle() -> &'static String {
| ^^^^^^^^
error: aborting due to previous error
For more information about this error, try `rustc --explain E0106`.
error: could not compile `ownership`
To learn more, run the command again with --verbose.
error: could not compile `ownership` due to previous error

View File

@ -1,7 +1,6 @@
[package]
name = "ownership"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
[dependencies]

View File

@ -3,11 +3,11 @@ fn main() {
}
// ANCHOR: here
fn dangle() -> &String { // dangle returns a reference to a String
fn dangle() -> &String { // / dangle 返回一个字符串的引用
let s = String::from("hello"); // s is a new String
let s = String::from("hello"); // s 是一个新字符串
&s // we return a reference to the String, s
} // Here, s goes out of scope, and is dropped. Its memory goes away.
// Danger!
&s // // 返回字符串 s 的引用
} // 这里 s 离开作用域并被丢弃。其内存被释放。
// 危险!
// ANCHOR_END: here

View File

@ -1,7 +1,6 @@
[package]
name = "ownership"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
[dependencies]

View File

@ -1,7 +1,6 @@
[package]
name = "ownership"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
[dependencies]

View File

@ -1,7 +1,6 @@
[package]
name = "ownership"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
[dependencies]

View File

@ -1,7 +1,6 @@
[package]
name = "ownership"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
[dependencies]

View File

@ -12,9 +12,5 @@ error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immuta
20 | println!("the first word is: {}", word);
| ---- immutable borrow later used here
error: aborting due to previous error
For more information about this error, try `rustc --explain E0502`.
error: could not compile `ownership`
To learn more, run the command again with --verbose.
error: could not compile `ownership` due to previous error

View File

@ -47,11 +47,7 @@ let s = "hello";
变量 `s` 绑定到了一个字符串字面值,这个字符串值是硬编码进程序代码中的。这个变量从声明的点开始直到当前 **作用域** 结束时都是有效的。示例 4-1 的注释标明了变量 `s` 在何处是有效的。
```rust
{ // s 在这里无效, 它尚未声明
let s = "hello"; // 从此处起s 是有效的
// 使用 s
} // 此作用域已结束s 不再有效
{{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-01/src/main.rs:here}}
```
<span class="caption">示例 4-1一个变量和其有效的作用域</span>
@ -65,9 +61,9 @@ let s = "hello";
### `String` 类型
为了演示所有权的规则,我们需要一个比第 3 章 [“数据类型”][data-types] 中讲到的都要复杂的数据类型。前面介绍的类型都是已知大小的,可以存储在栈中,并且当离开作用域时被移出栈,如果代码的另一部分需要在不同的作用域中使用相同的值,可以快速简单地复制它们来创建一个新的独立实例。不过我们需要寻找一个存储在堆上的数据来探索 Rust 是如何知道该在何时清理数据的。
为了演示所有权的规则,我们需要一个比第 3 章[“数据类型”][data-types]<!-- ignore -->中讲到的都要复杂的数据类型。前面介绍的类型都是已知大小的,可以存储在栈中,并且当离开作用域时被移出栈,如果代码的另一部分需要在不同的作用域中使用相同的值,可以快速简单地复制它们来创建一个新的独立实例。不过我们需要寻找一个存储在堆上的数据来探索 Rust 是如何知道该在何时清理数据的。
这里使用 `String` 作为例子,并专注于 `String` 与所有权相关的部分。这些方面也同样适用于标准库提供的或你自己创建的其他复杂数据类型。在第 8 章会更深入地讲解 `String`
这里使用 `String` 作为例子,并专注于 `String` 与所有权相关的部分。这些方面也同样适用于标准库提供的或你自己创建的其他复杂数据类型。在[第 8 章][ch8]<!-- ignore -->会更深入地讲解 `String`
我们已经见过字符串字面值即被硬编码进程序里的字符串值。字符串字面值是很方便的不过它们并不适合使用文本的每一种场景。原因之一就是它们是不可变的。另一个原因是并非所有字符串的值都能在编写代码时就知道例如要是想获取用户输入并存储该怎么办呢为此Rust 有第二个字符串类型,`String`。这个类型管理被分配到堆上的数据,所以能够存储在编译时未知大小的文本。可以使用 `from` 函数基于字符串字面值来创建 `String`,如下:
@ -75,16 +71,12 @@ let s = "hello";
let s = String::from("hello");
```
这两个冒号(`::`)是运算符,允许将特定的 `from` 函数置于 `String` 类型的命名空间namespace而不需要使用类似 `string_from` 这样的名字。在第 5 章的 [“方法语法”“Method Syntax”][method-syntax] 部分会着重讲解这个语法而且在第 7 章的 [“路径用于引用模块树中的项”][paths-module-tree] 中会讲到模块的命名空间。
这两个冒号(`::`)是运算符,允许将特定的 `from` 函数置于 `String` 类型的命名空间namespace而不需要使用类似 `string_from` 这样的名字。在第 5 章的[“方法语法”“Method Syntax”][method-syntax]<!-- ignore -->部分会着重讲解这个语法而且在第 7 章的[“路径用于引用模块树中的项”][paths-module-tree]<!-- ignore -->中会讲到模块的命名空间。
**可以** 修改此类字符串
```rust
let mut s = String::from("hello");
s.push_str(", world!"); // push_str() 在字符串后追加字面值
println!("{}", s); // 将打印 `hello, world!`
{{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-01-can-mutate-string/src/main.rs:here}}
```
那么这里有什么区别呢?为什么 `String` 可变而字面值却不行呢?区别在于两个类型对内存的处理上。
@ -105,15 +97,10 @@ println!("{}", s); // 将打印 `hello, world!`
Rust 采取了一个不同的策略:内存在拥有它的变量离开作用域后就被自动释放。下面是示例 4-1 中作用域例子的一个使用 `String` 而不是字符串字面值的版本:
```rust
{
let s = String::from("hello"); // 从此处起s 是有效的
// 使用 s
} // 此作用域已结束,
// s 不再有效
{{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-02-string-scope/src/main.rs:here}}
```
这是一个将 `String` 需要的内存返回给分配器的很自然的位置:当 `s` 离开作用域的时候。当变量离开作用域Rust 为我们调用一个特殊的函数。这个函数叫做 [`drop`][drop],在这里 `String` 的作者可以放置释放内存的代码。Rust 在结尾的 `}` 处自动调用 `drop`
这是一个将 `String` 需要的内存返回给分配器的很自然的位置:当 `s` 离开作用域的时候。当变量离开作用域Rust 为我们调用一个特殊的函数。这个函数叫做 [`drop`][drop]<!-- ignore -->,在这里 `String` 的作者可以放置释放内存的代码。Rust 在结尾的 `}` 处自动调用 `drop`
> 注意:在 C++ 中,这种 item 在生命周期结束时释放资源的模式有时被称作 **资源获取即初始化***Resource Acquisition Is Initialization (RAII)*)。如果你使用过 RAII 模式的话应该对 Rust 的 `drop` 函数并不陌生。
@ -124,8 +111,7 @@ Rust 采取了一个不同的策略:内存在拥有它的变量离开作用域
在 Rust 中,多个变量能够以不同的方式与同一数据交互。让我们看看示例 4-2 中一个使用整型的例子。
```rust
let x = 5;
let y = x;
{{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-02/src/main.rs:here}}
```
<span class="caption">示例 4-2将变量 `x` 的整数值赋给 `y`</span>
@ -135,8 +121,7 @@ let y = x;
现在看看这个 `String` 版本:
```rust
let s1 = String::from("hello");
let s2 = s1;
{{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-03-string-move/src/main.rs:here}}
```
这看起来与上面的代码非常类似,所以我们可能会假设他们的运行方式也是类似的:也就是说,第二行可能会生成一个 `s1` 的拷贝并绑定到 `s2` 上。不过,事实上并不完全是这样。
@ -166,26 +151,13 @@ let s2 = s1;
为了确保内存安全,这种场景下 Rust 的处理有另一个细节值得注意。在 `let s2 = s1` 之后Rust 认为 `s1` 不再有效,因此 Rust 不需要在 `s1` 离开作用域后清理任何东西。看看在 `s2` 被创建之后尝试使用 `s1` 会发生什么;这段代码不能运行:
```rust,ignore,does_not_compile
let s1 = String::from("hello");
let s2 = s1;
println!("{}, world!", s1);
{{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-04-cant-use-after-move/src/main.rs:here}}
```
你会得到一个类似如下的错误,因为 Rust 禁止你使用无效的引用。
```text
error[E0382]: use of moved value: `s1`
--> src/main.rs:5:28
|
3 | let s2 = s1;
| -- value moved here
4 |
5 | println!("{}, world!", s1);
| ^^ value used here after move
|
= note: move occurs because `s1` has type `std::string::String`, which does
not implement the `Copy` trait
```console
{{#include ../listings/ch04-understanding-ownership/no-listing-04-cant-use-after-move/output.txt}}
```
如果你在其他语言中听说过术语 **浅拷贝***shallow copy*)和 **深拷贝***deep copy*),那么拷贝指针、长度和容量而不拷贝数据可能听起来像浅拷贝。不过因为 Rust 同时使第一个变量无效了,这个操作被称为 **移动***move*),而不是浅拷贝。上面的例子可以解读为 `s1`**移动** 到了 `s2` 中。那么具体发生了什么,如图 4-4 所示。
@ -205,10 +177,7 @@ error[E0382]: use of moved value: `s1`
这是一个实际使用 `clone` 方法的例子:
```rust
let s1 = String::from("hello");
let s2 = s1.clone();
println!("s1 = {}, s2 = {}", s1, s2);
{{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-05-clone/src/main.rs:here}}
```
这段代码能正常运行,并且明确产生图 4-3 中行为,这里堆上的数据 **确实** 被复制了。
@ -220,17 +189,14 @@ println!("s1 = {}, s2 = {}", s1, s2);
这里还有一个没有提到的小窍门。这些代码使用了整型并且是有效的,他们是示例 4-2 中的一部分:
```rust
let x = 5;
let y = x;
println!("x = {}, y = {}", x, y);
{{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-06-copy/src/main.rs:here}}
```
但这段代码似乎与我们刚刚学到的内容相矛盾:没有调用 `clone`,不过 `x` 依然有效且没有被移动到 `y` 中。
原因是像整型这样的在编译时已知大小的类型被整个存储在栈上,所以拷贝其实际的值是快速的。这意味着没有理由在创建变量 `y` 后使 `x` 无效。换句话说,这里没有深浅拷贝的区别,所以这里调用 `clone` 并不会与通常的浅拷贝有什么不同,我们可以不用管它。
Rust 有一个叫做 `Copy` trait 的特殊注解,可以用在类似整型这样的存储在栈上的类型上(第 10 章详细讲解 trait。如果一个类型实现了 `Copy` trait那么一个旧的变量在将其赋值给其他变量后仍然可用。Rust 不允许自身或其任何部分实现了 `Drop` trait 的类型使用 `Copy` trait。如果我们对其值离开作用域时需要特殊处理的类型使用 `Copy` 注解,将会出现一个编译时错误。要学习如何为你的类型添加 `Copy` 注解以实现该 trait请阅读附录 C 中的 [“可派生的 trait”][derivable-traits]。
Rust 有一个叫做 `Copy` trait 的特殊注解,可以用在类似整型这样的存储在栈上的类型上(第 10 章详细讲解 trait。如果一个类型实现了 `Copy` trait那么一个旧的变量在将其赋值给其他变量后仍然可用。Rust 不允许自身或其任何部分实现了 `Drop` trait 的类型使用 `Copy` trait。如果我们对其值离开作用域时需要特殊处理的类型使用 `Copy` 注解,将会出现一个编译时错误。要学习如何为你的类型添加 `Copy` 注解以实现该 trait请阅读附录 C 中的 [“可派生的 trait”][derivable-traits]<!-- ignore -->
那么哪些类型实现了 `Copy` trait 呢?你可以查看给定类型的文档来确认,不过作为一个通用的规则,任何一组简单标量值的组合都可以实现 `Copy`,任何不需要分配内存或某种形式资源的类型都可以实现 `Copy` 。如下是一些 `Copy` 的类型:
@ -247,27 +213,7 @@ Rust 有一个叫做 `Copy` trait 的特殊注解,可以用在类似整型这
<span class="filename">文件名: src/main.rs</span>
```rust
fn main() {
let s = String::from("hello"); // s 进入作用域
takes_ownership(s); // s 的值移动到函数里 ...
// ... 所以到这里不再有效
let x = 5; // x 进入作用域
makes_copy(x); // x 应该移动函数里,
// 但 i32 是 Copy 的,所以在后面可继续使用 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 移出作用域。不会有特殊操作
{{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-03/src/main.rs}}
```
<span class="caption">示例 4-3带有所有权和作用域注释的函数</span>
@ -281,31 +227,7 @@ fn makes_copy(some_integer: i32) { // some_integer 进入作用域
<span class="filename">文件名: src/main.rs</span>
```rust
fn main() {
let s1 = gives_ownership(); // gives_ownership 将返回值
// 移给 s1
let s2 = String::from("hello"); // s2 进入作用域
let s3 = takes_and_gives_back(s2); // s2 被移动到
// takes_and_gives_back 中,
// 它也将返回值移给 s3
} // 这里, s3 移出作用域并被丢弃。s2 也移出作用域,但已被移走,
// 所以什么也不会发生。s1 移出作用域并被丢弃
fn gives_ownership() -> String { // gives_ownership 将返回值移动给
// 调用它的函数
let some_string = String::from("hello"); // some_string 进入作用域.
some_string // 返回 some_string 并移出给调用的函数
}
// takes_and_gives_back 将传入字符串并返回该值
fn takes_and_gives_back(a_string: String) -> String { // a_string 进入作用域
a_string // 返回 a_string 并移出给调用的函数
}
{{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-04/src/main.rs}}
```
<span class="caption">示例 4-4: 转移返回值的所有权</span>
@ -319,19 +241,7 @@ fn takes_and_gives_back(a_string: String) -> String { // a_string 进入作用
<span class="filename">文件名: src/main.rs</span>
```rust
fn main() {
let s1 = String::from("hello");
let (s2, len) = calculate_length(s1);
println!("The length of '{}' is {}.", s2, len);
}
fn calculate_length(s: String) -> (String, usize) {
let length = s.len(); // len() 返回字符串的长度
(s, length)
}
{{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-05/src/main.rs}}
```
<span class="caption">示例 4-5: 返回参数的所有权</span>
@ -339,6 +249,8 @@ fn calculate_length(s: String) -> (String, usize) {
但是这未免有些形式主义而且这种场景应该很常见。幸运的是Rust 对此提供了一个功能,叫做 **引用***references*)。
[data-types]: ch03-02-data-types.html#数据类型
[ch8]: ch08-02-strings.html
[derivable-traits]: appendix-03-derivable-traits.html
[method-syntax]: ch05-03-method-syntax.html#方法语法
[paths-module-tree]: ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html
[drop]: https://rustwiki.org/zh-CN/std/ops/trait.Drop.html#tymethod.drop

View File

@ -7,17 +7,7 @@
<span class="filename">文件名: src/main.rs</span>
```rust
fn main() {
let s1 = String::from("hello");
let len = calculate_length(&s1);
println!("The length of '{}' is {}.", s1, len);
}
fn calculate_length(s: &String) -> usize {
s.len()
}
{{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-07-reference/src/main.rs:all}}
```
首先,注意变量声明和函数返回值中的所有元组代码都消失了。其次,注意我们传递 `&s1``calculate_length`,同时在函数定义中,我们获取 `&String` 而不是 `String`
@ -33,12 +23,7 @@ fn calculate_length(s: &String) -> usize {
仔细看看这个函数调用:
```rust
# fn calculate_length(s: &String) -> usize {
# s.len()
# }
let s1 = String::from("hello");
let len = calculate_length(&s1);
{{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-07-reference/src/main.rs:here}}
```
`&s1` 语法让我们创建一个 **指向**`s1` 的引用,但是并不拥有它。因为并不拥有这个值,所以当引用停止使用时,它所指向的值也不会被丢弃。
@ -46,10 +31,7 @@ let len = calculate_length(&s1);
同理,函数签名使用 `&` 来表明参数 `s` 的类型是一个引用。让我们增加一些解释性的注释:
```rust
fn calculate_length(s: &String) -> usize { // s 是对 String 的引用
s.len()
} // 这里s 离开了作用域。但因为它并不拥有引用值的所有权,
// 所以什么也不会发生
{{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-08-reference-with-annotations/src/main.rs:here}}
```
变量 `s` 有效的作用域与函数参数的作用域一样,不过当引用停止使用时并不丢弃它指向的数据,因为我们没有所有权。当函数使用引用而不是实际值作为参数,无需返回值来交还所有权,因为就不曾拥有所有权。
@ -61,29 +43,15 @@ fn calculate_length(s: &String) -> usize { // s 是对 String 的引用
<span class="filename">文件名: src/main.rs</span>
```rust,ignore,does_not_compile
fn main() {
let s = String::from("hello");
change(&s);
}
fn change(some_string: &String) {
some_string.push_str(", world");
}
{{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-06/src/main.rs}}
```
<span class="caption">示例 4-6尝试修改借用的值</span>
这里是错误:
```text
error[E0596]: cannot borrow immutable borrowed content `*some_string` as mutable
--> error.rs:8:5
|
7 | fn change(some_string: &String) {
| ------- use `&mut String` here to make mutable
8 | some_string.push_str(", world");
| ^^^^^^^^^^^ cannot borrow as mutable
```console
{{#include ../listings/ch04-understanding-ownership/listing-04-06/output.txt}}
```
正如变量默认是不可变的,引用也一样。(默认)不允许修改引用的值。
@ -95,15 +63,7 @@ error[E0596]: cannot borrow immutable borrowed content `*some_string` as mutable
<span class="filename">文件名: src/main.rs</span>
```rust
fn main() {
let mut s = String::from("hello");
change(&mut s);
}
fn change(some_string: &mut String) {
some_string.push_str(", world");
}
{{#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` 函数将改变它所借用的值。
@ -113,28 +73,13 @@ fn change(some_string: &mut String) {
<span class="filename">文件名: src/main.rs</span>
```rust,ignore,does_not_compile
let mut s = String::from("hello");
let r1 = &mut s;
let r2 = &mut s;
println!("{}, {}", r1, r2);
{{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-10-multiple-mut-not-allowed/src/main.rs:here}}
```
错误如下:
```text
error[E0499]: cannot borrow `s` as mutable more than once at a time
--> src/main.rs:5:14
|
4 | let r1 = &mut s;
| ------ first mutable borrow occurs here
5 | let r2 = &mut s;
| ^^^^^^ second mutable borrow occurs here
6 |
7 | println!("{}, {}", r1, r2);
| -- first borrow later used here
```console
{{#include ../listings/ch04-understanding-ownership/no-listing-10-multiple-mut-not-allowed/output.txt}}
```
这个报错说这段代码是无效的,因为我们不能在同一时间多次将 `s` 作为可变变量借用。第一个可变的借入在 `r1` 中,并且必须持续到在 `println` 中使用它,但是在那个可变引用的创建和它的使用之间,我们又尝试在 `r2` 中创建另一个可变引用,该引用借用与 `r1` 相同的数据。
@ -152,61 +97,30 @@ error[E0499]: cannot borrow `s` as mutable more than once at a time
一如既往,可以使用大括号来创建一个新的作用域,以允许拥有多个可变引用,只是不能 **同时** 拥有:
```rust
let mut s = String::from("hello");
{
let r1 = &mut s;
} // r1 在这里离开了作用域,所以我们完全可以创建一个新的引用
let r2 = &mut s;
{{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-11-muts-in-separate-scopes/src/main.rs:here}}
```
类似的规则也存在于同时使用可变与不可变引用中。这些代码会导致一个错误:
```rust,ignore,does_not_compile
let mut s = String::from("hello");
let r1 = &s; // 没问题
let r2 = &s; // 没问题
let r3 = &mut s; // 大问题
println!("{}, {}, and {}", r1, r2, r3);
{{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-12-immutable-and-mutable-not-allowed/src/main.rs:here}}
```
错误如下:
```text
error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable
--> src/main.rs:6:14
|
4 | let r1 = &s; // no problem
| -- immutable borrow occurs here
5 | let r2 = &s; // no problem
6 | let r3 = &mut s; // BIG PROBLEM
| ^^^^^^ mutable borrow occurs here
7 |
8 | println!("{}, {}, and {}", r1, r2, r3);
| -- immutable borrow later used here
```console
{{#include ../listings/ch04-understanding-ownership/no-listing-12-immutable-and-mutable-not-allowed/output.txt}}
```
哇哦!我们 **也** 不能在拥有不可变引用的同时拥有可变引用。不可变引用的用户可不希望在他们的眼皮底下值就被意外的改变了!然而,多个不可变引用是可以的,因为没有哪个只能读取数据的人有能力影响其他人读取到的数据。
注意一个引用的作用域从声明的地方开始一直持续到最后一次使用为止。例如,因为最后一次使用不可变引用(`println!`),发生在声明可变引用之前,所以如下代码是可以编译的:
```rust,edition2018,ignore
let mut s = String::from("hello");
let r1 = &s; // 没问题
let r2 = &s; // 没问题
println!("{} and {}", r1, r2);
// 此位置之后 r1 和 r2 不再使用
let r3 = &mut s; // 没问题
println!("{}", r3);
```rust,edition2018
{{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-13-reference-scope-ends/src/main.rs:here}}
```
不可变引用 `r1``r2` 的作用域在 `println!` 最后一次使用之后结束,这也是创建可变引用 `r3` 的地方。它们的作用域没有重叠所以代码是可以编译的。编译器在作用域结束之前判断不再使用的引用的能力被称为非词法作用域生命周期Non-Lexical Lifetimes简称 NLL。你可以在 [The Edition Guide][nll] 中阅读更多关于它的信息。
不可变引用 `r1``r2` 的作用域在 `println!` 最后一次使用之后结束,这也是创建可变引用 `r3` 的地方。它们的作用域没有重叠所以代码是可以编译的。编译器在作用域结束之前判断不再使用的引用的能力被称为非词法作用域生命周期Non-Lexical Lifetimes简称 NLL。你可以在 [“Rust 2018 版说明”][nll] 中阅读更多关于它的信息。
尽管这些错误有时使人沮丧,但请牢记这是 Rust 编译器在提前指出一个潜在的 bug在编译时而不是在运行时并精准显示问题所在。这样你就不必去跟踪为何数据并不是你想象中的那样。
@ -219,35 +133,19 @@ println!("{}", r3);
<span class="filename">文件名: src/main.rs</span>
```rust,ignore,does_not_compile
fn main() {
let reference_to_nothing = dangle();
}
fn dangle() -> &String {
let s = String::from("hello");
&s
}
{{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-14-dangling-reference/src/main.rs}}
```
这里是错误:
```text
error[E0106]: missing lifetime specifier
--> main.rs:5:16
|
5 | fn dangle() -> &String {
| ^ expected lifetime parameter
|
= help: this function's return type contains a borrowed value, but there is
no value for it to be borrowed from
= help: consider giving it a 'static lifetime
```console
{{#include ../listings/ch04-understanding-ownership/no-listing-14-dangling-reference/output.txt}}
```
错误信息引用了一个我们还未介绍的功能生命周期lifetimes。第 10 章会详细介绍生命周期。不过,如果你不理会生命周期部分,错误信息中确实包含了为什么这段代码有问题的关键信息:
```text
this function's return type contains a borrowed value, but there is no value for it to be borrowed from.
this function's return type contains a borrowed value, but there is no value for it to be borrowed from
```
让我们仔细看看我们的 `dangle` 代码的每一步到底发生了什么:
@ -255,13 +153,7 @@ this function's return type contains a borrowed value, but there is no value for
<span class="filename">文件名: src/main.rs</span>
```rust,ignore,does_not_compile
fn dangle() -> &String { // dangle 返回一个字符串的引用
let s = String::from("hello"); // s 是一个新字符串
&s // 返回字符串 s 的引用
} // 这里 s 离开作用域并被丢弃。其内存被释放。
// 危险!
{{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-15-dangling-reference-annotated/src/main.rs:here}}
```
因为 `s` 是在 `dangle` 函数内创建的,当 `dangle` 的代码执行完毕后,`s` 将被释放。不过我们尝试返回它的引用。这意味着这个引用会指向一个无效的 `String`这可不对Rust 不会允许我们这么做。
@ -269,11 +161,7 @@ fn dangle() -> &String { // dangle 返回一个字符串的引用
这里的解决方法是直接返回 `String`
```rust
fn no_dangle() -> String {
let s = String::from("hello");
s
}
{{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-16-no-dangle/src/main.rs:here}}
```
这样就没有任何错误了。所有权被移动出去,所以没有值被释放。

View File

@ -15,17 +15,7 @@ fn first_word(s: &String) -> ?
<span class="filename">文件名: src/main.rs</span>
```rust
fn first_word(s: &String) -> usize {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return i;
}
}
s.len()
}
{{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-07/src/main.rs:here}}
```
<span class="caption">示例 4-7`first_word` 函数返回 `String` 参数的一个字节索引值</span>
@ -33,28 +23,23 @@ fn first_word(s: &String) -> usize {
因为需要逐个元素的检查 `String` 中的值是否为空格,需要用 `as_bytes` 方法将 `String` 转化为字节数组:
```rust,ignore
let bytes = s.as_bytes();
{{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-07/src/main.rs:as_bytes}}
```
接下来,使用 `iter` 方法在字节数组上创建一个迭代器:
```rust,ignore
for (i, &item) in bytes.iter().enumerate() {
{{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-07/src/main.rs:iter}}
```
我们将在第 13 章详细讨论迭代器。现在,只需知道 `iter` 方法返回集合中的每一个元素,而 `enumerate` 包装了 `iter` 的结果,将这些元素作为元组的一部分来返回。`enumerate` 返回的元组中,第一个元素是索引,第二个元素是集合中元素的引用。这比我们自己计算索引要方便一些。
我们将在[第 13 章][ch13]<!-- ignore -->详细讨论迭代器。现在,只需知道 `iter` 方法返回集合中的每一个元素,而 `enumerate` 包装了 `iter` 的结果,将这些元素作为元组的一部分来返回。`enumerate` 返回的元组中,第一个元素是索引,第二个元素是集合中元素的引用。这比我们自己计算索引要方便一些。
因为 `enumerate` 方法返回一个元组,我们可以使用模式来解构,我们将在第 6 章中进一步讨论有关模式的问题。所以在 `for` 循环中,我们指定了一个模式,其中元组中的 `i` 是索引而元组中的 `&item` 是单个字节。因为我们从 `.iter().enumerate()` 中获取了集合元素的引用,所以模式中使用了 `&`
因为 `enumerate` 方法返回一个元组,我们可以使用模式来解构,我们将在[第 6 章][ch6]<!-- ignore -->中进一步讨论有关模式的问题。所以在 `for` 循环中,我们指定了一个模式,其中元组中的 `i` 是索引而元组中的 `&item` 是单个字节。因为我们从 `.iter().enumerate()` 中获取了集合元素的引用,所以模式中使用了 `&`
`for` 循环中,我们通过字节的字面值语法来寻找代表空格的字节。如果找到了一个空格,返回它的位置。否则,使用 `s.len()` 返回字符串的长度:
```rust,ignore
if item == b' ' {
return i;
}
}
s.len()
{{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-07/src/main.rs:inside_for}}
```
现在有了一个找到字符串中第一个单词结尾索引的方法,不过这有一个问题。我们返回了一个独立的 `usize`,不过它只在 `&String` 的上下文中才是一个有意义的数字。换句话说,因为它是一个与 `String` 相分离的值,无法保证将来它仍然有效。考虑一下示例 4-8 中使用了示例 4-7 中 `first_word` 函数的程序。
@ -62,28 +47,7 @@ s.len()
<span class="filename">文件名: src/main.rs</span>
```rust
# fn first_word(s: &String) -> usize {
# let bytes = s.as_bytes();
#
# for (i, &item) in bytes.iter().enumerate() {
# if item == b' ' {
# return i;
# }
# }
#
# s.len()
# }
#
fn main() {
let mut s = String::from("hello world");
let word = first_word(&s); // word 的值为 5
s.clear(); // 这清空了字符串,使其等于 ""
// word 在此处的值仍然是 5
// 但是没有更多的字符串让我们可以有效地应用数值 5。word 的值现在完全无效!
}
{{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-08/src/main.rs:here}}
```
<span class="caption">示例 4-8存储 `first_word` 函数调用的返回值并接着改变 `String` 的内容</span>
@ -105,10 +69,7 @@ fn second_word(s: &String) -> (usize, usize) {
**字符串 slice***string slice*)是 `String` 中一部分值的引用,它看起来像这样:
```rust
let s = String::from("hello world");
let hello = &s[0..5];
let world = &s[6..11];
{{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-17-slice/src/main.rs:here}}
```
这类似于引用整个 `String` 不过带有额外的 `[0..5]` 部分。它不是对整个 `String` 的引用,而是对部分 `String` 的引用。
@ -152,24 +113,14 @@ let slice = &s[0..len];
let slice = &s[..];
```
> 注意:字符串 slice range 的索引必须位于有效的 UTF-8 字符边界内,如果尝试从一个多字节字符的中间位置创建字符串 slice则程序将会因错误而退出。出于介绍字符串 slice 的目的,本部分假设只使用 ASCII 字符集;第 8 章的 [“使用字符串存储 UTF-8 编码的文本”][strings] 部分会更加全面的讨论 UTF-8 处理问题。
> 注意:字符串 slice range 的索引必须位于有效的 UTF-8 字符边界内,如果尝试从一个多字节字符的中间位置创建字符串 slice则程序将会因错误而退出。出于介绍字符串 slice 的目的,本部分假设只使用 ASCII 字符集;第 8 章的[“使用字符串存储 UTF-8 编码的文本”][strings]<!-- ignore -->部分会更加全面的讨论 UTF-8 处理问题。
在记住所有这些知识后,让我们重写 `first_word` 来返回一个 slice。“字符串 slice” 的类型声明写作 `&str`
<span class="filename">文件名: src/main.rs</span>
```rust
fn first_word(s: &String) -> &str {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[0..i];
}
}
&s[..]
}
{{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-18-first-word-slice/src/main.rs:here}}
```
我们使用跟示例 4-7 相同的方式获取单词结尾的索引,通过寻找第一个出现的空格。当找到一个空格,我们返回一个字符串 slice它使用字符串的开始和空格的索引作为开始和结束的索引。
@ -187,31 +138,13 @@ fn second_word(s: &String) -> &str {
<span class="filename">文件名: src/main.rs</span>
```rust,ignore,does_not_compile
fn main() {
let mut s = String::from("hello world");
let word = first_word(&s);
s.clear(); // 错误!
println!("the first word is: {}", word);
}
{{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-19-slice-error/src/main.rs:here}}
```
这里是编译错误:
```text
error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable
--> src/main.rs:18:5
|
16 | let word = first_word(&s);
| -- immutable borrow occurs here
17 |
18 | s.clear(); // error!
| ^^^^^^^^^ mutable borrow occurs here
19 |
20 | println!("the first word is: {}", word);
| ---- immutable borrow later used here
```console
{{#include ../listings/ch04-understanding-ownership/no-listing-19-slice-error/output.txt}}
```
回忆一下借用规则,当拥有某值的不可变引用时,就不能再获取一个可变引用。因为 `clear` 需要清空 `String`,它尝试获取一个可变引用。在调用 `clear` 之后的 `println!` 使用了 `word` 中的引用所以这个不可变的引用在此时必须仍然有效。Rust 不允许 `clear` 中的可变引用和 `word` 中的不可变引用同时存在因此编译失败。Rust 不仅使得我们的 API 简单易用,也在编译时就消除了一整类的错误!
@ -237,42 +170,17 @@ fn first_word(s: &String) -> &str {
而更有经验的 Rustacean 会编写出示例 4-9 中的签名,因为它使得可以对 `String` 值和 `&str` 值使用相同的函数:
```rust,ignore
fn first_word(s: &str) -> &str {
{{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-09/src/main.rs:here}}
```
<span class="caption">示例 4-9: 通过将 `s` 参数的类型改为字符串 slice 来改进 `first_word` 函数</span>
如果有一个字符串 slice可以直接传递它。如果有一个 `String`,则可以传递整个 `String` 的 slice 或对 `String` 的引用。这种灵活性利用了 *deref coercions* 的优势,这个特性我们将在[“函数和方法的隐式解引用强制转换”][deref-coercions]章节中介绍。定义一个获取字符串 slice 而不是 `String` 引用的函数使得我们的 API 更加通用并且不会丢失任何功能:
如果有一个字符串 slice可以直接传递它。如果有一个 `String`,则可以传递整个 `String` 的 slice 或对 `String` 的引用。这种灵活性利用了 *deref coercions* 的优势,这个特性我们将在[“函数和方法的隐式解引用强制转换”][deref-coercions]<!-- ignore -->章节中介绍。定义一个获取字符串 slice 而不是 `String` 引用的函数使得我们的 API 更加通用并且不会丢失任何功能:
<span class="filename">文件名: src/main.rs</span>
```rust
# fn first_word(s: &str) -> &str {
# let bytes = s.as_bytes();
#
# for (i, &item) in bytes.iter().enumerate() {
# if item == b' ' {
# return &s[0..i];
# }
# }
#
# &s[..]
# }
fn main() {
let my_string = String::from("hello world");
// first_word 中传入 `String` 的 slice
let word = first_word(&my_string[..]);
let my_string_literal = "hello world";
// first_word 中传入字符串字面值的 slice
let word = first_word(&my_string_literal[..]);
// 因为字符串字面值 **就是** 字符串 slice
// 这样写也可以,即不使用 slice 语法!
let word = first_word(my_string_literal);
}
{{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-09/src/main.rs:usage}}
```
### 其他类型的 slice
@ -299,5 +207,7 @@ let slice = &a[1..3];
所有权系统影响了 Rust 中很多其他部分的工作方式,所以我们还会继续讲到这些概念,这将贯穿本书的余下内容。让我们开始第 5 章,来看看如何将多份数据组合进一个 `struct` 中。
[ch13]: ch13-02-iterators.html
[ch6]: ch06-02-match.html#patterns-that-bind-to-values
[strings]: ch08-02-strings.html#使用字符串存储-utf-8-编码的文本
[deref-coercions]: ch15-02-deref.html#函数和方法的隐式解引用强制转换