From bcf5c27c0586195c435061d42e64b06d4b20c90d Mon Sep 17 00:00:00 2001 From: sunface Date: Tue, 30 Nov 2021 16:27:35 +0800 Subject: [PATCH] add content to variable.md --- .DS_Store | Bin 8196 -> 8196 bytes assets/ferris.css | 2 +- assets/ferris.js | 15 +- book.toml | 9 +- codes/basic/.DS_Store | Bin 0 -> 6148 bytes {assets => codes/basic/variable}/.DS_Store | Bin 6148 -> 6148 bytes codes/basic/variable/01/Cargo.lock | 6 + codes/basic/variable/01/Cargo.toml | 7 + codes/basic/variable/01/output.txt | 20 ++ codes/basic/variable/01/src/main.rs | 6 + src/.DS_Store | Bin 0 -> 8196 bytes src/SUMMARY.md | 2 +- src/appendix/a-keywords | 1 - src/appendix/a-keywords.md | 2 +- src/basic/img/.DS_Store | Bin 0 -> 6148 bytes src/basic/img/ferris/does_not_compile.svg | 71 ++++++ src/basic/img/ferris/not_desired_behavior.svg | 75 ++++++ src/basic/img/ferris/panics.svg | 70 ++++++ src/basic/variable.md | 160 ++++++++---- src/first-try/cargo.md | 5 +- src/style-guide/naming.md | 229 ++++++++++++++++++ 21 files changed, 616 insertions(+), 64 deletions(-) create mode 100644 codes/basic/.DS_Store rename {assets => codes/basic/variable}/.DS_Store (79%) create mode 100644 codes/basic/variable/01/Cargo.lock create mode 100644 codes/basic/variable/01/Cargo.toml create mode 100644 codes/basic/variable/01/output.txt create mode 100644 codes/basic/variable/01/src/main.rs create mode 100644 src/.DS_Store delete mode 100644 src/appendix/a-keywords create mode 100644 src/basic/img/.DS_Store create mode 100644 src/basic/img/ferris/does_not_compile.svg create mode 100644 src/basic/img/ferris/not_desired_behavior.svg create mode 100644 src/basic/img/ferris/panics.svg diff --git a/.DS_Store b/.DS_Store index d946b493d6811093bbd6b65012d91f1b24050c05..10449272a96f0a546a951284f83c7d8bf007f583 100644 GIT binary patch delta 184 zcmZp1XmOa}&nUVvU^hRb=w=>)I#zaOhGK>yhUCewMH|^oOm!5D%*`fW5U^K=ikAf! z<>ln(r86)vFaogvLpe}&CPN7j=P{%+=uVz3YR{-U`L^hDAbD6UVKci#Gqnwk1pr(Q BFfafB delta 74 zcmZp1XmOa}&nUbxU^hRb@Ma!?I@ZbS!~_`QC)bPF1Ihhj&w=D}@q~#DPd2klG~<+g F2mq~K8p;3w diff --git a/assets/ferris.css b/assets/ferris.css index 37120792..b856d477 100644 --- a/assets/ferris.css +++ b/assets/ferris.css @@ -30,4 +30,4 @@ body.ayu .not_desired_behavior { .ferris-explain { width: 100px; -} +} \ No newline at end of file diff --git a/assets/ferris.js b/assets/ferris.js index f3f631e2..9bacc4ef 100644 --- a/assets/ferris.js +++ b/assets/ferris.js @@ -1,15 +1,19 @@ var ferrisTypes = [ { attr: 'does_not_compile', - title: '此程式碼無法編譯!' + title: 'This code does not compile!' }, { attr: 'panics', - title: '此程式碼會恐慌!' + title: 'This code panics!' + }, + { + attr: 'unsafe', + title: 'This code block contains unsafe code.' }, { attr: 'not_desired_behavior', - title: '此程式碼沒有產生預期的行為。' + title: 'This code does not produce the desired behavior.' } ] @@ -32,16 +36,17 @@ function attachFerrises (type) { } function attachFerris (element, type) { + alert('1') var a = document.createElement('a') a.setAttribute('href', 'ch00-00-introduction.html#ferris') a.setAttribute('target', '_blank') var img = document.createElement('img') - img.setAttribute('src', 'img/ferris/' + type.attr + '.svg') + img.setAttribute('src', '/img/ferris/' + type.attr + '.svg') img.setAttribute('title', type.title) img.className = 'ferris' a.appendChild(img) element.parentElement.insertBefore(a, element) -} +} \ No newline at end of file diff --git a/book.toml b/book.toml index 0c8a80fe..125ee2eb 100644 --- a/book.toml +++ b/book.toml @@ -1,11 +1,10 @@ [book] authors = ["sunface"] -language = "en" -multilingual = false -src = "src" -title = "The way to rust" +language = "zh-CN" +title = "Rust编程指南(The Way To Rust)" [output.html] additional-css = ["assets/ferris.css", "assets/theme/2018-edition.css"] additional-js = ["assets/ferris.js"] -git-repository-url = "https://github.com/rustcm/the-way-to-rust" \ No newline at end of file +git-repository-url = "https://github.com/rustcm/the-way-to-rust" +edit-url-template = "https://github.com/rustcm/the-way-to-rust/edit/main/{path}" \ No newline at end of file diff --git a/codes/basic/.DS_Store b/codes/basic/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..2bdbae3b399a3615d6d9fc2885848ed222437933 GIT binary patch literal 6148 zcmeHKOKQVF43(NJ4BdE{QUNMJ1%4E;??Ztb*2F2$KOG1@0syy2 zyJ79K1h7~FSQDo}WMCRpU{Ezj3=KN+CF^S96c}{T96mIktT~~mKOOfMFBh$Wj8uRM z%oW(gwzdBM48Jk|&q>@-0V?pX6wqzgcYC~2_SVJAS+6bd7r512;AU7m1;N`f(AzOK f){b{x6m`YcIIoFQpwp3eI*>mDrVEV<+*^SwO_CLJ literal 0 HcmV?d00001 diff --git a/assets/.DS_Store b/codes/basic/variable/.DS_Store similarity index 79% rename from assets/.DS_Store rename to codes/basic/variable/.DS_Store index 94ebaff9ac48fd6b4c1e1bb18830f1820db396ec..aca29e62175019d11be7d921f08d1c1109fc5063 100644 GIT binary patch delta 114 zcmZoMXfc@JFUrioz`)4BAi$86lb-}+3jnd>=84S98FfKYObiALhMqb3$;i?`alQXw p0AxYcOkTy*$Zl?;qhMrWKG}%bo)xTfa}9Gh%fyC?&Fmb1`2kp68ixP? delta 244 zcmZoMXfc@JFUrcmz`)4BAi%(o$xzIo$B+ypi#H20FK4U=NpUfxF{CmS0U?sKXHI@{ zQcivnP#&m{u^UM1{Raaei-Ca?O-~j>F`5opG##uAB@7urdvY045h@kHhX2H^bg~6g pBfE*Ij)IZ7+2jLE_Tos!lm!>%<>cq319dY3eN)e}nVsV=KLDr;Jx~Au diff --git a/codes/basic/variable/01/Cargo.lock b/codes/basic/variable/01/Cargo.lock new file mode 100644 index 00000000..2d62cbe7 --- /dev/null +++ b/codes/basic/variable/01/Cargo.lock @@ -0,0 +1,6 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "variables" +version = "0.1.0" + diff --git a/codes/basic/variable/01/Cargo.toml b/codes/basic/variable/01/Cargo.toml new file mode 100644 index 00000000..7915d397 --- /dev/null +++ b/codes/basic/variable/01/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "variables" +version = "0.1.0" +authors = ["Your Name "] +edition = "2018" + +[dependencies] diff --git a/codes/basic/variable/01/output.txt b/codes/basic/variable/01/output.txt new file mode 100644 index 00000000..7c2ec5bf --- /dev/null +++ b/codes/basic/variable/01/output.txt @@ -0,0 +1,20 @@ +$ cargo run + Compiling variables v0.1.0 (file:///projects/variables) +error[E0384]: cannot assign twice to immutable variable `x` + --> src/main.rs:4:5 + | +2 | let x = 5; + | - + | | + | first assignment to `x` + | help: make this binding mutable: `mut x` +3 | println!("The value of x is: {}", x); +4 | x = 6; + | ^^^^^ cannot assign twice to immutable variable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0384`. +error: could not compile `variables` + +To learn more, run the command again with --verbose. diff --git a/codes/basic/variable/01/src/main.rs b/codes/basic/variable/01/src/main.rs new file mode 100644 index 00000000..a6c7ac07 --- /dev/null +++ b/codes/basic/variable/01/src/main.rs @@ -0,0 +1,6 @@ +fn main() { + let x = 5; + println!("The value of x is: {}", x); + x = 6; + println!("The value of x is: {}", x); +} diff --git a/src/.DS_Store b/src/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..eda83cf19952388d1835c2bdea7a413b9be5f65e GIT binary patch literal 8196 zcmeHMO>fgc5S>la)*(=Opi(bLmbkX1lv0Gaq@f%*aB27u0#xicq%N+va*~Fqs+2SQ z2mS(A{s{aRPVi=STiH%}ZIRfGcE_>b+u40PYiArHQq4hhk7${QJXE%ebu?27U*|ee zD)!70tOB1Xr8ae*CAu9Z6KHqBDc}@v3OEIv0#1Q{K><9oxp)^m_svz;It82p|D^(a zfACS+Hmn^RE4L0bk^;b1aaszFkp~FJx3*#J*jS;WPm?_;OI0?-P==1{KEq)f){c!e zbW(;+%6e8dLs8-#JYV3X8pgWTDc}^CRDg5$c?u|^W3;*aU5B{Bn`s+$?T-{3@vDG< zj_~OK9Z^i5O}>AQ|6an+0^g$%_gzG)+Y`u8u<-=)>jbh5WJX=mr9OX`kX5{q&_LR^ z5@(~VbW6AzeZ*WcjI>Xys2PolJy(8~7&E{hQn!dw@UIqfYFSh`>>Y)Pc>fU|C2_C6 zzWz&9W~#Gu^WMBy_ujYm^{~|s`h#9G$lmg|7g{Ib*>MoQiaW!-#amBw+7IHilL;Y? zI)J=;9jB2VHuWHldctj)3tr8u?JX{!oNR19Sn)R=Zk?|9CyzGPSNzTUTc@WrZ|Tn6 z$GdxQv*%>CH49B=Yi{I{fUT_YD27 zVywIq;JK&?t6hl1IgBX9+60(aX*Up*E?_-bMV<%mvtAj`dKd5uG{lHTz_V6b#@E59 zrmQNfo#VRaKf=frr@$2`Fk^f!*I}Pt{Qmz6M&?{P1)KtxrGTpLw04^CcJ|Y+?3i+d|5FRTaO@!y$JQ82Up;5_V)yOp&g6%YL|?k74S?DZk!>LQGLYTQ<% zxR>f&&rAqMINeHhGHEn-YqGJ|n%3lGzfrHr=1yxmb;SC{*1>7_F?x#Ci+*ax;r-OI zYH}4YAk2VplL}~3xot7HNe92QajwP8ph;)kHXq#HncEJ9 z>$}7Cr4DD@HAo{dKn% + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/basic/img/ferris/not_desired_behavior.svg b/src/basic/img/ferris/not_desired_behavior.svg new file mode 100644 index 00000000..47f40245 --- /dev/null +++ b/src/basic/img/ferris/not_desired_behavior.svg @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/basic/img/ferris/panics.svg b/src/basic/img/ferris/panics.svg new file mode 100644 index 00000000..be55f5e0 --- /dev/null +++ b/src/basic/img/ferris/panics.svg @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/basic/variable.md b/src/basic/variable.md index 93cf45c3..917c2187 100644 --- a/src/basic/variable.md +++ b/src/basic/variable.md @@ -1,5 +1,7 @@ # 变量与绑定 +> 本文在内容上部分参考了Rust官方编程那本书 + 鉴于本书的[目标读者](../intro.md)已经熟练掌握其它任意一门编程语言,因此这里就不再对何为变量进行赘述,让我们开门见山来谈谈,为何Rust选择了手动设定变量可变性。 ## 为何要手动设置变量的可变性? @@ -8,80 +10,106 @@ 能想要学习Rust,说明我们的读者都是相当有水平的程序员了,你们应该能理解一切选择都是权衡,那么两者都要的权衡是什么呢?这就是Rust开发团队为我们做出的贡献,两者都要意味着底层代码的实现复杂度大幅提升,Respect to The Rust Team! -除了以上两个优点,还有一个很大的优点,那就是运行性能上的提升,因为不可变的变量在运行期会避免一些多余的runtime检查。 +除了以上两个优点,还有一个很大的优点,那就是运行性能上的提升,因为将本身无需改变的变量声明为不可变在运行期会避免一些多余的runtime检查。 ## 变量命名 在命名方面,和其它语言没有区别,不过当你给变量命名时,需要遵循[Rust命名规范](../style-guide/naming.md)。 -> ### 关键字 -> > Rust语言有一些**关键字**(*keywords*),和其他语言一样,这些关键字都是被保留给Rust语言使用的,因此,这些关键字不能被用作变量或函数的名称。 在[附录 A](../appendix/a-keywords) 中可找到关键字列表。 -Keywords +## 变量绑定 -The Rust language has a set of keywords that are reserved for use by the language only, much as in other languages. Keep in mind that you cannot use these words as names of variables or functions. Most of the keywords have special meanings, and you’ll be using them to do various tasks in your Rust programs; a few have no current functionality associated with them but have been reserved for functionality that might be added to Rust in the future. You can find a list of the keywords in Appendix A. +在其它语言中,我们用`var a = "hello world"`的方式给a赋值,也就是把等式右边的`"hello world`"字符串赋值给变量`a`,而在Rust中,我们这样写:`let a = "hello world"`, 同时给这个过程起了另一个名字:**变量绑定**。 -,默认情况下变量是**不可变的**(*immutable*)。这是 Rust 中众多精妙之处的其中一个,Rust 的这些设计点鼓励你以一种充分利用 Rust 提供的安全和简单并发的方式来编写代码。不过你也可以选择让变量是**可变的**(*mutable*)。让我们来探讨为什么 Rust 鼓励你选用不可变性,以及为什么你可能不喜欢这样。 +为何不用赋值而用绑定呢(其实你也可以称之为赋值,但是绑定的含义更清晰准确)?这里就涉及Rust最核心的原则-**所有权**,简单来讲,任何内存对象都是有主人的,而且一般情况下完全属于它的主人,绑定就是把这个对象绑定给一个变量,让这个变量成为它的主人(聪明的读者应该能猜到,在这种情况下,该对象之前的主人就会丧失对该对象的所有权),像极了我们的现实世界,不是吗? -当变量不可变时,这意味着一旦一个值绑定到一个变量名后,就不能再更改该值了。为了说明,我们在 *projects* 目录下使用 `cargo new variables` 来创建一个名为 *variables* 新项目。 +至于为何要采用所有权这种复杂的东东,先别急,等讲[Rust核心概念](../core/intro.md)时,我们会为你详细道来。 -然后在新建的 *variables* 目录下,打开 *src/main.rs* 并将代码替换为下面还未能通过编译的代码: +## 变量可变性 + +Rust的变量在默认情况下是**不可变的**。在上文提到过,这是Rust团队为我们精心设计的语言特性之一,这样可以让我们编写更安全、更高性能的代码。当然你可以通过`mut`关键字让变量变为**可变的**,以实现更加灵活的设计。 + +当变量不可变时,这意味着一旦一个值绑定到一个变量`a`后,就不能再更改`a`的值了。为了说明,在我们的工程目录下使用 `cargo new variables`来创建一个名为 *variables* 的新项目。 -文件名:src/main.rs +然后在新建的 *variables* 目录下,打开 *src/main.rs* 并将代码替换为下面还未能通过编译的代码: ```rust,ignore,does_not_compile -{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-01-variables-are-immutable/src/main.rs}} +fn main() { + let x = 5; + println!("The value of x is: {}", x); + x = 6; + println!("The value of x is: {}", x); +} ``` - -保存文件,并使用 `cargo run` 运行程序。你将会收到一条错误消息,输出如下所示: + +保存文件,并使用 `cargo run`运行程序或者。你将会收到一条错误消息,输出如下所示: ```console -{{#include ../listings/ch03-common-programming-concepts/no-listing-01-variables-are-immutable/output.txt}} +$ cargo run + Compiling variables v0.1.0 (file:///projects/variables) +error[E0384]: cannot assign twice to immutable variable `x` + --> src/main.rs:4:5 + | +2 | let x = 5; + | - + | | + | first assignment to `x` + | help: consider making this binding mutable: `mut x` +3 | println!("The value of x is: {}", x); +4 | x = 6; + | ^^^^^ cannot assign twice to immutable variable + +error: aborting due to previous error ``` -这个例子展示了编译器如何帮助你查找程序中的错误。即使编译器错误可能令人沮丧,它们也只是表明你的程序做你想做的事情并不安全;并**不**意味着你不是一个好开发者!有经验的 Rustaceans 依然会遇到编译错误。 - -上面的错误指出错误的原因是 `cannot -assign twice to immutable variable x`(不能对不可变变量二次赋值),因为我们尝试给不可变的 `x` 变量赋值为第二个值。 - -当我们尝试改变一个前面指定为不可变的值时我们会得到编译期错误,这点很重要,因为这种情况很可能导致 bug。如果我们代码的一部分假设某个值永远不会更改,而代码的另一部分更改了该值,那很可能第一部分代码不会按照所设计的逻辑运行。这个 bug 的根源在实际开发中可能很难追踪,特别是第二部分代码只是**偶尔**变更了原来的值。 +具体的错误原因是 `cannot assign twice to immutable variable x`(对不可变的变量无法进行二次再赋值),因为我们尝试给不可变的 `x` 变量赋予第二个值。 -在 Rust 中,编译器保证了当我们声明了一个值不会改变,那它就真的不可改变。这意味着当你正在阅读和编写代码时,就不必跟踪一个值怎样变化以及在哪发生改变,这可以使得代码更容易理解。 +这种错误是为了避免无法预期的错误发生在我们的变量上:一部分代码假定该变量的值永远不会改变,而另外一部分代码却无情的改变了这个值,在实际开发过程中,这个错误是很难被发现的,特别是在多线程编程中。 -但可变性有时也相当重要。变量只是默认不可变的;我们可以通过在变量名前加上 `mut` 使得它们可变。除了允许这个值改变外,它还向以后的读代码的人传达了这样的意思:代码的其他部分将会改变这个变量值。 +这种规则让我们的代码变得非常清晰,只有你想让你的变量改变时,它才能改变,这样就不会造成心智上的负担,也给别人阅读代码带来便利。 -例如将 *src/main.rs* 改为以下内容: +但是可变性也非常重要,否则我们就要像ClosureScript中那样,每次要改变,就是重新生成一个对象,在拥有大量对象的场景,性能会变得非常低下,内存拷贝的成本异常的高。 -文件名:src/main.rs +在Rust中,可变性很简单,只要在变量名前加一个`mut`即可, 而且这种显式的声明方式还会给后来人传达这样的信息:嗯,这个变量在后面代码部分会发生改变。 +为了让变量可变(挺拗口的),将 *src/main.rs* 改为以下内容: ```rust -{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-02-adding-mut/src/main.rs}} +fn main() { + let mut x = 5; + println!("The value of x is: {}", x); + x = 6; + println!("The value of x is: {}", x); +} ``` 运行程序将得到下面结果: ```console -{{#include ../listings/ch03-common-programming-concepts/no-listing-02-adding-mut/output.txt}} +$ cargo run + Compiling variables v0.1.0 (file:///projects/variables) + Finished dev [unoptimized + debuginfo] target(s) in 0.30s + Running `target/debug/variables` +The value of x is: 5 +The value of x is: 6 ``` -加上 `mut` 后,我们就可以将 `x` 绑定的值从 `5` 改成 `6`。在一些情况下,你需要变量是可变的,因为相比只使用不可变变量的实现,这可使得代码更容易编写。 +一切抉择都是权衡,使用可变还是不可变,更多的还是取决于你的选择,例如不可变可以带来安全性,但是丧失了灵活性和性能(如果你要改变,就要重新创建一个新的变量,这里涉及到内存对象的再分配)。而可变变量最大的好处就是使用上的灵活和性能上的提升。 -除了预防 bug 外,还有很多权衡要考虑。例如,在使用大型数据结构的情形下,在同一位置更改实例可能比复制并返回新分配的实例要更快。使用较小的数据结构时,通常创建新的实例并以更具函数式的风格来编写程序,可能会更容易理解,所以值得以较低的性能开销来确保代码清晰。 +例如,在使用大型数据结构或者热点代码路径(被大量频繁调用)的情形下,在同一内存位置更新实例可能比复制并返回新分配的实例要更快。使用较小的数据结构时,通常创建新的实例并以更具函数式的风格来编写程序,可能会更容易理解,所以值得以较低的性能开销来确保代码清晰。 ### 变量和常量之间的差异 变量的值不能更改可能让你想起其他另一个很多语言都有的编程概念:**常量**(*constant*)。与不可变变量一样,常量也是绑定到一个常量名且不允许更改的值,但是常量和变量之间存在一些差异。 -首先,常量不允许使用 `mut`。常量不仅仅默认不可变,而且自始至终不可变。 +首先,常量不允许使用 `mut`。**常量不仅仅默认不可变,而且自始至终不可变**。 -常量使用 `const` 关键字而不是 `let` 关键字来声明,并且值的类型**必须**标注。我们将在下一节[“数据类型”][data-types]中介绍类型和类型标注,因此现在暂时不需关心细节。只需知道你必须始终对类型进行标注。 +常量使用 `const` 关键字而不是 `let` 关键字来声明,并且值的类型**必须**标注。我们将在下一节[数据类型](./type.md)中介绍,因此现在暂时无需关心细节。 -常量可以在任意作用域内声明,包括全局作用域,这对于代码中很多部分都需要知道一个值的情况特别有用。 -最后一个不同点是常量只能设置为常量表达式,而不能是函数调用的结果或是只能在运行时计算得到的值。 + +最后一个不同点是常量只能设置为常量表达式,而不能是函数调用的结果或是只能在运行时计算得到的值,例如你可以这样来声明常量: 下面是一个常量声明的例子,其常量名为 `MAX_POINTS`,值设置为 100,000。(Rust 常量的命名约定是全部字母都使用大写,并使用下划线分隔单词,另外对数字字面量可插入下划线以提高可读性): @@ -89,42 +117,76 @@ assign twice to immutable variable x`(不能对不可变变量二次赋值) const MAX_POINTS: u32 = 100_000; ``` -在声明的作用域内,常量在程序运行的整个过程中都有效。对于应用程序域中程序的多个部分可能都需要知道的值的时候,常量是一个很有用的选择,例如游戏中允许玩家赚取的最大点数或光速。 +常量可以在任意作用域内声明,包括全局作用域,在声明的作用域内,常量在程序运行的整个过程中都有效。对于需要在多处代码共享一个不可变的值时非常有用,例如游戏中允许玩家赚取的最大点数或光速。 -将整个程序中用到的硬编码(hardcode)值命名为常量,对于将该值的含义传达给代码的未来维护者很有用。如果将来需要更改硬编码的值,则只需要在代码中改动一处就可以了。 +>在实际使用中,最好将程序中用到的硬编码值都声明为常量,对于代码后续的维护有莫大的帮助。如果将来需要更改硬编码的值,你也只需要在代码中更改一处即可。 -### 变量遮蔽 +### 变量遮蔽(shadowing) -正如你在第 2 章[“猜数字游戏”][comparing-the-guess-to-the-secret-number]章节中所看到的,你可以声明和前面变量具有相同名称的新变量。Rustaceans 说这个是第一个变量被第二个变量**遮蔽**(*shadow*),这意味着当我们使用变量时我们看到的会是第二个变量的值。我们可以通过使用相同的变量名并重复使用 `let` 关键字来遮蔽变量,如下所示: +Rust允许声明相同的变量名,在后面声明的变量会遮蔽掉前面声明的, 如下所示: -文件名:src/main.rs ```rust -{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-03-shadowing/src/main.rs}} +fn main() { + let x = 5; + // 在main函数的作用域内对之前的x进行遮蔽 + let x = x + 1; + + { + // 在当前的花括号作用域内,对之前的x进行遮蔽 + let x = x * 2; + println!("The value of x in the inner scope is: {}", x); + } + + println!("The value of x is: {}", x); +} ``` -这个程序首先将数值 `5` 绑定到 `x`。然后通过重复使用 `let x =` 来遮蔽之前的 `x`,并取原来的值加上 `1`,所以 `x` 的值变成了 `6`。第三个 `let` 语句同样遮蔽前面的 `x`,取之前的值并乘上 `2`,得到的 `x` 最终值为 `12`。当运行此程序,将输出以下内容: +这个程序首先将数值 `5` 绑定到 `x`,然后通过重复使用 `let x =` 来遮蔽之前的 `x`,并取原来的值加上 `1`,所以 `x` 的值变成了 `6`。第三个 `let` 语句同样遮蔽前面的 `x`,取之前的值并乘上 `2`,得到的 `x` 最终值为 `12`。当运行此程序,将输出以下内容: ```console -{{#include ../listings/ch03-common-programming-concepts/no-listing-03-shadowing/output.txt}} +$ cargo run + Compiling variables v0.1.0 (file:///projects/variables) + ... +The value of x is: 12 ``` -这和将变量标记为 `mut` 的方式不同,因为除非我们再次使用 `let` 关键字,否则若是我们不小心尝试重新赋值给这个变量,我们将得到一个编译错误。通过使用 `let`,我们可以对一个值进行一些转换,但在这些转换完成后,变量将是不可变的。 +这和`mut`变量的使用是不同的,第二个let生成了完全不同的新变量,两个变量只是恰好拥有同样的名称,涉及一次内存对象的再分配 +,而`mut`声明的变量,可以修改同一个内存地址上的值,并不会发生内存对象的再分配,性能要好很多。 + +变量遮蔽的用处在于,如果你在某个作用域内无需再使用之前的变量(在被遮蔽后,无法再访问到之前的同名变量),就可以重复的使用变量名字,而不用绞尽脑汁去想更多的名字。 -`mut` 和变量遮蔽之间的另一个区别是,因为我们在再次使用 `let` 关键字时有效地创建了一个新的变量,所以我们可以改变值的类型,但重复使用相同的名称。例如,假设我们程序要求用户输入空格字符来显示他们想要的空格数目,但我们实际上想要将该输入存储为一个数字: +例如,假设我们程序要求用户输入空格字符来显示他们想要的空格数目,但我们实际上想要将该输入存储为一个数字: ```rust -{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-04-shadowing-can-change-types/src/main.rs:here}} + // 字符串类型 + let spaces = " "; + // usize数值类型 + let spaces = spaces.len(); ``` -这种结构是允许的,因为第一个 `spaces` 变量是一个字符串类型,第二个 `spaces` 变量是一个全新的变量且和第第一个具有相同的变量名,且是一个数字类型。所以变量遮蔽可以让我们就不必给出不同的名称,如 `spaces_str` 和 `spaces_num`;相反我们可以重复使用更简单的 `spaces` 变量名。然而,如果我们对此尝试使用 `mut`,如下所示,我们将得到一个编译期错误: +这种结构是允许的,因为第一个 `spaces` 变量是一个字符串类型,第二个 `spaces` 变量是一个全新的变量且和第第一个具有相同的变量名,且是一个数值类型。所以变量遮蔽可以让我们就不必给出不同的名称,如 `spaces_str` 和 `spaces_num`;相反我们可以重复使用更简单的 `spaces` 变量名。然而,如果我们对此尝试使用 `mut`,如下所示,我们将得到一个编译期错误: -```rust,ignore,does_not_compile -{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-05-mut-cant-change-types/src/main.rs:here}} +```rust, + let mut spaces = " "; + spaces = spaces.len(); ``` -该错误表明我们不允许更改变量的类型: +运行一下 ```console -{{#include ../listings/ch03-common-programming-concepts/no-listing-05-mut-cant-change-types/output.txt}} -``` \ No newline at end of file +$ cargo run + Compiling variables v0.1.0 (file:///projects/variables) +error[E0308]: mismatched types + --> src/main.rs:3:14 + | +3 | spaces = spaces.len(); + | ^^^^^^^^^^^^ expected `&str`, found `usize` + +error: aborting due to previous error +``` + +该错误标明,我们试图把一个`usize`类型的数值赋值为一个字符串变量。 + + +至此,关于变量的内容你已经彻底掌握了,下面来学习下Rust的基本数据类型。 \ No newline at end of file diff --git a/src/first-try/cargo.md b/src/first-try/cargo.md index 233e0f9b..9d1cb639 100644 --- a/src/first-try/cargo.md +++ b/src/first-try/cargo.md @@ -1 +1,4 @@ -## 认识Cargo \ No newline at end of file +## 认识Cargo + + +## cargo check \ No newline at end of file diff --git a/src/style-guide/naming.md b/src/style-guide/naming.md index 6951f9ed..ff247df6 100644 --- a/src/style-guide/naming.md +++ b/src/style-guide/naming.md @@ -1 +1,230 @@ # 命名规范 + +基本的Rust命名规范在[RFC 430]中有描述. + +通常,对于"type-level"的构造Rust倾向于使用驼峰命名,而对于'value-level'的构造使用蛇形命名。详情如下: + +| 条目 | 惯例 | +| ---- | ---------- | +| 包Crates | [unclear](https://github.com/rust-lang/api-guidelines/issues/29) | +| 模块Modules | `snake_case` | +| 类型Types | `UpperCamelCase` | +| 特征Traits | `UpperCamelCase` | +| 枚举变量Enum variants | `UpperCamelCase` | +| 函数Functions | `snake_case` | +| 方法Methods | `snake_case` | +| 通用构造器General constructors | `new` or `with_more_details` | +| 转换构造器Conversion constructors | `from_some_other_type` | +| 宏Macros | `snake_case!` | +| 局部变量Local variables | `snake_case` | +| 静态类型Statics | `SCREAMING_SNAKE_CASE` | +| 常量Constants | `SCREAMING_SNAKE_CASE` | +| 类型参数Type parameters | `UpperCamelCase`, 通常使用一个大写字母: `T` | +| 生命周期Lifetimes | 通常使用小写字母: `'a`, `'de`, `'src` | +| Features | [unclear](https://github.com/rust-lang/api-guidelines/issues/101) but see [C-FEATURE] | + +对于驼峰命名法, 复合词的缩略形式我们认为是一个单独的词语,所以只对首字母进行大写: 使用`Uuid`而不是`UUID`, `Usize`而不是`USize`, `Stdin`而不是`StdIn`.对于蛇形命名法,缩略词用全小写: `is_xid_start`. + +对于蛇形命名(包括全大写的`SCREAMING_SNAKE_CASE`), 除了最后一部分,其它部分的词语都不能由单个字母组成: +`btree_map`而不是`b_tree_map`, `PI_2`而不是`PI2`. + +包名不应该使用`-rs`或者`-rust`作为后缀,因为每一个包都是Rust写的,因此这种多余的注释其实没有任何意义。 + +[RFC 430]: https://github.com/rust-lang/rfcs/blob/master/text/0430-finalizing-naming-conventions.md +[C-FEATURE]: #c-feature + + +## 类型转换要遵守`as_`, `to_`, `into_`命名惯例(C-CONV) +类型转换应该通过方法调用的方式实现,其中的前缀规则如下: + +| 方法前缀 | 性能开销 | 所有权改变 | +| ------ | ---- | --------- | +| `as_` | Free | borrowed -\> borrowed | +| `to_` | Expensive | borrowed -\> borrowed
borrowed -\> owned (non-Copy types)
owned -\> owned (Copy types) | +| `into_` | Variable | owned -\> owned (non-Copy types) | + +For example: + +- [`str::as_bytes()`] 把`str`变成UTF-8字节数组, 性能开销是0. 其中输入是一个借用的`&str`,输出也是一个借用的`&str`. +- [`Path::to_str`] 会执行一次昂贵的UTF-8字节数组检查,输入和输出都是借用的。对于这种情况,如果把方法命名为`as_str`是不正确的,因为这个方法的开销还挺大. +- [`str::to_lowercase()`]在调用过程中会遍历字符串的字符,且可能会分配新的内存对象.输入是一个借用的`str`,输出是一个有独立所有权的`String` +- [`String::into_bytes()`]返回`String`底层的`Vec`数组,转换本身是零消耗的。该方法获取`String`的所有权,然后返回一个新的有独立所有权的`Vec` + + +[`str::as_bytes()`]: https://doc.rust-lang.org/std/primitive.str.html#method.as_bytes +[`Path::to_str`]: https://doc.rust-lang.org/std/path/struct.Path.html#method.to_str +[`str::to_lowercase()`]: https://doc.rust-lang.org/std/primitive.str.html#method.to_lowercase +[`f64::to_radians()`]: https://doc.rust-lang.org/std/primitive.f64.html#method.to_radians +[`String::into_bytes()`]: https://doc.rust-lang.org/std/string/struct.String.html#method.into_bytes +[`BufReader::into_inner()`]: https://doc.rust-lang.org/std/io/struct.BufReader.html#method.into_inner +[`BufWriter::into_inner()`]: https://doc.rust-lang.org/std/io/struct.BufWriter.html#method.into_inner + + +当一个单独的值被某个类型所包装时,访问该类型的内部值应通过`into_inner()`方法来访问。例如将一个缓冲区值包装为[`BufReader`]类型,还有[`GzDecoder`]、[`AtomicBool`]等,都是这种类型。 + + +[`BufReader`]: https://doc.rust-lang.org/std/io/struct.BufReader.html#method.into_inner +[`GzDecoder`]: https://docs.rs/flate2/0.2.19/flate2/read/struct.GzDecoder.html#method.into_inner +[`AtomicBool`]: https://doc.rust-lang.org/std/sync/atomic/struct.AtomicBool.html#method.into_inner + +如果`mut`限定符在返回类型中出现,那么在命名上也应该体现出来。例如,[`Vec::as_mut_slice`] 就说明它返回了一个mut切片,在这种情况下`as_mut_slice`比`as_slice_mut`更适合。 + +[`Vec::as_mut_slice`]: https://doc.rust-lang.org/std/vec/struct.Vec.html#method.as_mut_slice + +```rust +// 返回类型是一个mut切片. +fn as_mut_slice(&mut self) -> &mut [T]; +``` + +##### 标准库中的一些例子 + +- [`Result::as_ref`](https://doc.rust-lang.org/std/result/enum.Result.html#method.as_ref) +- [`RefCell::as_ptr`](https://doc.rust-lang.org/std/cell/struct.RefCell.html#method.as_ptr) +- [`slice::to_vec`](https://doc.rust-lang.org/std/primitive.slice.html#method.to_vec) +- [`Option::into_iter`](https://doc.rust-lang.org/std/option/enum.Option.html#method.into_iter) + + +## 读访问器(Getter)的名称遵循Rust的命名规范(C-GETTER) + +除了少数例外,在Rust代码中`get`前缀不用于getter。 + +```rust +pub struct S { + first: First, + second: Second, +} + +impl S { + // 而不是get_first + pub fn first(&self) -> &First { + &self.first + } + + // 而不是get_first_mut, get_mut_first, or mut_first. + pub fn first_mut(&mut self) -> &mut First { + &mut self.first + } +} +``` +至于上文提到的少数例外,如下:当有且仅有一个值能被getter所获取时,才使用`get`前缀。例如, +[`Cell::get`]能直接访问到`Cell`中的内容。 + +[`Cell::get`]: https://doc.rust-lang.org/std/cell/struct.Cell.html#method.get + +有些getter会在过程中执行运行时检查,那么我们就可以考虑添加`_unchecked`getter函数,这个函数虽然不安全,但是往往具有更高的性能, +典型的例子如下: + +```rust +fn get(&self, index: K) -> Option<&V>; +fn get_mut(&mut self, index: K) -> Option<&mut V>; +unsafe fn get_unchecked(&self, index: K) -> &V; +unsafe fn get_unchecked_mut(&mut self, index: K) -> &mut V; +``` + +[`TempDir::path`]: https://docs.rs/tempdir/0.3.5/tempdir/struct.TempDir.html#method.path +[`TempDir::into_path`]: https://docs.rs/tempdir/0.3.5/tempdir/struct.TempDir.html#method.into_path + +### 标准库示例 + +- [`std::io::Cursor::get_mut`](https://doc.rust-lang.org/std/io/struct.Cursor.html#method.get_mut) +- [`std::ptr::Unique::get_mut`](https://doc.rust-lang.org/std/ptr/struct.Unique.html#method.get_mut) +- [`std::sync::PoisonError::get_mut`](https://doc.rust-lang.org/std/sync/struct.PoisonError.html#method.get_mut) +- [`std::sync::atomic::AtomicBool::get_mut`](https://doc.rust-lang.org/std/sync/atomic/struct.AtomicBool.html#method.get_mut) +- [`std::collections::hash_map::OccupiedEntry::get_mut`](https://doc.rust-lang.org/std/collections/hash_map/struct.OccupiedEntry.html#method.get_mut) +- [`<[T]>::get_unchecked`](https://doc.rust-lang.org/std/primitive.slice.html#method.get_unchecked) + +## 一个集合上的方法,如果返回迭代器,需遵循命名规则:`iter`, `iter_mut`, `into_iter` (C-ITER) + +```rust +fn iter(&self) -> Iter // Iter implements Iterator +fn iter_mut(&mut self) -> IterMut // IterMut implements Iterator +fn into_iter(self) -> IntoIter // IntoIter implements Iterator +``` +上面的规则适用于同构性的数据集合。与之相反,`str`类型是一个utf8字节数组切片,与同构性集合有一点微妙的差别,它可以认为是字节集合,也可以认为是字符集合,因此它提供了[`str::bytes`]去遍历字节,还有[`str::chars`]去遍历字符,而并没有直接定义`iter`等方法。 + +[`str::bytes`]: https://doc.rust-lang.org/std/primitive.str.html#method.bytes +[`str::chars`]: https://doc.rust-lang.org/std/primitive.str.html#method.chars + +上述规则只适用于方法,并不适用于函数。例如`url`包的[`percent_encode`]函数返回一个迭代器用于遍历百分比编码([Percent encoding](https://en.wikipedia.org/wiki/Percent-encoding))的字符串片段. 在这种情况下,使用`iter`/`iter_mut`/`into_iter`诸如此类的函数命名无法表达任何具体的含义。 + +[`percent_encode`]: https://docs.rs/url/1.4.0/url/percent_encoding/fn.percent_encode.html +[RFC 199]: https://github.com/rust-lang/rfcs/blob/master/text/0199-ownership-variants.md + +### 标准库示例 + +- [`Vec::iter`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.iter) +- [`Vec::iter_mut`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.iter_mut) +- [`Vec::into_iter`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.into_iter) +- [`BTreeMap::iter`](https://doc.rust-lang.org/std/collections/struct.BTreeMap.html#method.iter) +- [`BTreeMap::iter_mut`](https://doc.rust-lang.org/std/collections/struct.BTreeMap.html#method.iter_mut) + +## 迭代器的类型应该与产生它的方法名相匹配(C-ITER-TY) +例如形如`into_iter()`的方法应该返回一个`IntoIter`类型,与之相似,其它任何返回迭代器的方法也应该遵循这种命名惯例。 + +上述规则主要应用于方法,但是经常对于函数也适用。例如上文提到的url包中的[`percent_encode`]函数,返回了一个[`PercentEncode`]类型. + +[PercentEncode-type]: https://docs.rs/url/1.4.0/url/percent_encoding/struct.PercentEncode.html + +特别是,当这些类型跟包名前缀一起使用时,将具备非常清晰的含义,例如[`vec::IntoIter`]. + +[`vec::IntoIter`]: https://doc.rust-lang.org/std/vec/struct.IntoIter.html + +### 标准库示例 + +* [`Vec::iter`] returns [`Iter`][slice::Iter] +* [`Vec::iter_mut`] returns [`IterMut`][slice::IterMut] +* [`Vec::into_iter`] returns [`IntoIter`][vec::IntoIter] +* [`BTreeMap::keys`] returns [`Keys`][btree_map::Keys] +* [`BTreeMap::values`] returns [`Values`][btree_map::Values] + +[`Vec::iter`]: https://doc.rust-lang.org/std/vec/struct.Vec.html#method.iter +[slice::Iter]: https://doc.rust-lang.org/std/slice/struct.Iter.html +[`Vec::iter_mut`]: https://doc.rust-lang.org/std/vec/struct.Vec.html#method.iter_mut +[slice::IterMut]: https://doc.rust-lang.org/std/slice/struct.IterMut.html +[`Vec::into_iter`]: https://doc.rust-lang.org/std/vec/struct.Vec.html#method.into_iter +[vec::IntoIter]: https://doc.rust-lang.org/std/vec/struct.IntoIter.html +[`BTreeMap::keys`]: https://doc.rust-lang.org/std/collections/struct.BTreeMap.html#method.keys +[btree_map::Keys]: https://doc.rust-lang.org/std/collections/btree_map/struct.Keys.html +[`BTreeMap::values`]: https://doc.rust-lang.org/std/collections/struct.BTreeMap.html#method.values +[btree_map::Values]: https://doc.rust-lang.org/std/collections/btree_map/struct.Values.html + + + +## Cargo Feature的名称不应该包含占位词(C-FEATURE) + +不要在[Cargo feature]中包含无法传达任何意义的词,例如`use-abc`或`with-abc`,直接命名为`abc`即可。 + +[Cargo feature]: http://doc.crates.io/manifest.html#the-features-section + +一个典型的例子就是:一个包对标准库有可选性的依赖。标准的写法如下: + +```toml +# 在Cargo.toml中 + +[features] +default = ["std"] +std = [] +``` + +```rust +// 在我们自定义的lib.rs中 + +#![cfg_attr(not(feature = "std"), no_std)] +``` +除了`std`之外,不要使用任何`ust-std`或者`with-std`等自以为很有创造性的名称。 + +## 命名要使用一致性的词序(C-WORD-ORDER) + +这是一些标准库中的错误类型: + +- [`JoinPathsError`](https://doc.rust-lang.org/std/env/struct.JoinPathsError.html) +- [`ParseBoolError`](https://doc.rust-lang.org/std/str/struct.ParseBoolError.html) +- [`ParseCharError`](https://doc.rust-lang.org/std/char/struct.ParseCharError.html) +- [`ParseFloatError`](https://doc.rust-lang.org/std/num/struct.ParseFloatError.html) +- [`ParseIntError`](https://doc.rust-lang.org/std/num/struct.ParseIntError.html) +- [`RecvTimeoutError`](https://doc.rust-lang.org/std/sync/mpsc/enum.RecvTimeoutError.html) +- [`StripPrefixError`](https://doc.rust-lang.org/std/path/struct.StripPrefixError.html) + +它们都使用了`谓语-宾语-错误`的词序,如果我们想要表达一个网络地址无法分析的错误,由于词序一致性的原则,命名应该如下`ParseAddrError`,而不是`AddrParseError`。 + +词序和个人习惯有很大关系,想要注意的是,你可以选择合适的词序,但是要在包的范畴内保持一致性,就如标准库中的包一样。