From 60bd1a1a61c6ce92b3562b397413a469313b0038 Mon Sep 17 00:00:00 2001 From: "rust-lang.xfoss.com" Date: Tue, 12 Dec 2023 17:58:19 +0800 Subject: [PATCH] Refining Ch04. --- projects/string_demo/src/main.rs | 10 +++------- src/ownership/about_ownership.md | 32 +++++++++++++++++++++----------- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/projects/string_demo/src/main.rs b/projects/string_demo/src/main.rs index 458fe32..6f89f5b 100644 --- a/projects/string_demo/src/main.rs +++ b/projects/string_demo/src/main.rs @@ -1,11 +1,7 @@ fn main() { - let s = "नमस्ते"; + let mut s = String::from("hello"); - for c in s.chars() { - println!("{}", c); - } + s.push_str(", world!"); // push_str() 方法会追加一个字面值,到某个 String - for b in s.bytes() { - println!("{}", b); - } + println! ("{}", s); // 这将打印出 `hello, world!` } diff --git a/src/ownership/about_ownership.md b/src/ownership/about_ownership.md index c2a65b2..3e447ca 100644 --- a/src/ownership/about_ownership.md +++ b/src/ownership/about_ownership.md @@ -106,28 +106,38 @@ let s = String::from("hello"); 这种字符串,*可* 被改变: ```rust -let mut s = String::from("hello"); -s.push_str(", world!"); // push_str() 方法会追加一个字面值,到某个 String -println! ("{}", s); // 这将打印出 `hello, world!` + let mut s = String::from("hello"); + + s.push_str(", world!"); // push_str() 方法会追加一个字面值,到某个 String + + println! ("{}", s); // 这将打印出 `hello, world!` ``` -那么,到底字面值, `&str` 与 `String` 类型有何不同?为何 `String` 可以被改变,而字面值却不能?区别就在于,这两种类型处理内存的方式,是不同的。 + +那么,这里有什么区别呢?为什么 `String` 可被改变,而字面值却不能呢?区别在于,这两种类型如何处理内存。 ## 内存与内存分配 -对于字符串字面值这种情况,在编译时咱们就知道其内容,因此该文本就被直接硬编码到了最终的可执行文件。这就是为何字符串字面值快速高效的原因。然而这些属性,只是来源于字符串字面值的不可变性。不幸的是,对于那些在编译时大小未知的,且在运行期间大小可能改变的各个文本,是无法为他们而将某块内存,放入到二进制程序中的(unfortunately, we can't put a blob of memory into the binary for each piece of text whose size is unknown at compile time and whose size might change while running the program)。 +**Memeory and Allocation** -在 `String` 类型下,为了支持可变、可增长的一段文本,就需要在内存堆上分配某个数量的内存,用来保存文本的那些内容,而这个数量在编译时则是未知的。这就意味着: -- 该内存必须在运行时向内存分配器请求; -- 在使用完那个 `String` 值之后,需要把这片内存交回给内存分配器的某种途径。 +对于字符串字面值,我们在编译时就知道其内容,因此该文本会被直接硬编码到,最终的可执行文件中。这就是字符串字面值快速高效的原因。但这些属性,只是来自于字符串字面值的不变性。遗憾的是,我们无法为每段编译时大小未知,且在运行程序时大小可能改变的文本,添加一块内存到二进制程序中。 -其中第一部分是由代码编写者完成的:在调用 `String::from` 时,这个 `from` 方法的实现,就请求了他所需的内存。在各种编程语言中,这是相当通行的做法。 +而在 `String` 类型下,为了支持某段可变、可增长的文本,我们需要在堆上,分配编译时未知的某个数量的内存,来保存内容。这意味着: -然而,这第二部分就有所不同了。在带有 *垃圾收集器,garbage collector, GC* 的那些语言中,对那些不再是正被使用中的内存的追踪和清理,是由垃圾收集器完成的,对此这里无需去考虑。而在大多数不带垃圾收集器的语言,就要靠代码编写者自己,去识别内存在何时不再被使用,并像请求内存时一样,要调用代码来显式地释放他。要正确完成这样的内存释放,早已成为一个历史悠久的编程难题。若忘记了,咱们就将浪费内存。而过早地释放内存,则将造成变量失效。若执行两次,那也同样是程序错误。咱们需要严格地一个 `allocate` 对应一个 `free`。 -Rust 采取了不同的路线:一旦某个变量超出了作用域,那么该变量所持有的内存空间,就被自动退回。下面是对清单 4-1 那个作用域示例,使用 `String` 而非字符串字面值的一个版本: +- 必须在运行时,向内存分配器申请该内存; + +- 我们需要某种,当咱们是使用完咱们的 `String` 后,将此内存返回给分配器的方法。 + + +第一部分是由我们完成的:当我们调用 `String::from` 时,其实现会请求所需的内存。这在编程语言中非常普遍。 + +不过,第二部分有所不同。在有 *垃圾回收器,garbage collector,GC* 的语言中,GC 会跟踪并清理不再使用的内存,我们不需要考虑这个问题。在大多数没有 GC 的语言中,我们有责任识别内存何时不再被使用,并调用代码显式释放内存,就像我们请求内存时一样。正确做到这一点,历来是编程中的难题。如果我们忘记了,就会浪费内存。如果过早释放,就会产生无效变量。如果我们做了两次,那也是一个错误。我们需要在 `allocate` 一次内存的同时,严格 `free` 一次内存。 + +Rust 采用了不同的路径:一旦拥有内存的变量超出作用域,该内存就会自动返回。下面是清单 4-1 中,咱们作用域示例的一个使用了 `String`,而非字符串字面值的版本: + ```rust {