坏蛋Dan
知乎@坏蛋Dan
发布时间:2024.1.4

前言

昨天我们学了rust中的一些高级特性

坏蛋Dan:rust基础学习--day37

今天本来是要开始实现最终的项目的,但是想了下先看下目录里的内容再来做应该会好些。


附录

有些内容到时候忘了回来看下这个附录或许会有所帮助。

附录A:关键字

这些关键字是函数、变量、参数、结构体的字段、模块、crates、常量、宏、静态值、属性、类型、traits以及生命周期的名字。

目前正在使用的关键字

由于知乎的编辑器的功能比较简单,这里就直接用Typora转成图片输出了。


没有实用性但是保留的关键字

目前有些字段是没有实用性的,但是确实是存在rust中的,因为将来可能会用到。


原始标识符(raw indentifier)

关键字一般是不允许在不符合要求的地方使用的,比如你的函数名叫做match,这样是不允许的。

这会导致抽象语法树构建失败,因为你这个关键字用错地方,状态机解析文件会失败。

但有的人就是头铁,就是要用。

rust其实允许我们通过r#这个前缀来使用这些关键字。

比如:

这样就能正常使用了,但是实际上这么做和改个名字又有何区别呢。。。都是为了让文件解析不会出错。

实际上还是有的,因为有的时候是因为一些标准的变化导致某个词突然变成关键字,比如try在2015不是关键字但是到了2018就变成关键字了,这个时候就难受。

所以为了避免这种问题,如果你觉得你用的某个词很有可能在将来变成一个关键字,那你就加上r#,这样就能确保将来标准变了之后也不会有事。

如果直接使用match会导致下面的报错


附录B:运算符(Operator)和符号(Symbol)

附录B包含一些rust的语法术语,包含运算符和一些特殊的符号,它们可能出现在上下文环境中,比如泛型、trait bounds、宏、属性、注释、元组、括号等。

运算符

img_operators

表格有些丑,因为typora缩小再导出表格图片会有覆盖的bug,所以这里就没有缩小。

突然发现知乎可以上传文档,解析出来的表格还行,但是想了下还是放图片算了。

重载是我们之前学的知识,可以通过实现std::ops里的trait来自定义操作符的功能。

忘了的同学直接不及格~


非运算符号(non-operator symbols)

非运算的符号,它们不像函数/方法调用。


附录C:可派生(derivable)trait

之前我们有用过derive这个属性,比如#[derive(Debug)]用在struct/enum上。

学完宏这一章节之后,我们知道了这其中的原理,虽然仅仅只是浅浅的学了表层。

它是一种派生宏,它会自动给这个struct/enum实现std::fmt::Debug这个trait

不过Debug只是标准库中其中一种派生属性和trait,接下来我们来看下标准库中还有哪些。

每一栏都包含下面几点

  • 有什么运算符和方法可以派生这个trait
  • 这个trait能干啥
  • 实现这个trait对于这个类型来说意味着什么
  • 可以/不能实现这个trait的条件
  • 例子

只有这些由标准库提供的trait能通过派生宏给你的类型实现,其它都不行。

其中一个例子就是Display这个trait,它可以用于格式化输出内容,但是它不能通过derive来实现。

我们的附录不会有太多全面的内容,所以如果你想了解更多,可以直接看这个文档

std - Rustdoc.rust-lang.org/std/index.html

Debug

老熟人了,可以让我们的数据格式化为字符串的形式并输出到终端。比如使用{:?}或者dbg!

其他就不都说了,例子的话,还记得我们的assert_eq!这个宏吗?它就是实现了这个trait,我们可以给这个宏传入第二第三个参数,这样在fail的时候就可以输出我们想要的错误提示语。

这好像有些跑题。。。。

我们来搞个struct来通过派生宏实现这个Debug


partialEqEq用于等价比较

PartialEq这个trait允许我们比较两个类型的实例,判断它们是否相等,并且可以使用==!=这两个运算符。

它会帮你实现eq这个方法,当你的struct实现了这个trait,他就能被用于比较两个实例是否字段都相等。而如果是enum,每一个变体都等于它自己并且不等于其它变体。

搞个例子,还是assert_eq!这个宏,因为它有需要比较两个实例的功能需求,所以这个宏内部实现了这个trait

Eq这个trait没有方法,它存在的目的就是为了表示这个值它等价于自己。

这个trait的高阶traitPartialEq,也就是说你要实现Eq这个trait之前你得先实现PartialEq

这样就能用=或者!=运算符比较自己了,比如HashMap,它就能比较两个key值是否相同。

当然,反过来并不行,并不是实现了PartialEq的都能实现Eq

比如浮动数字类型的其中一种数据NaN,它就没办法等价于自己。

官方给的例子都是口述的,醉了,附录就是这么没有面子

我们来写一个例子实现这俩trait

可以看到我们使用==并没有报错。


PartialOrdOrd用于排序比较

PartialOrd这个trait允许你基于排序的目的去比较同一个类型的两个实例。

如果一个类型实现了PartialOrd这个trait,那么它就能使用<, >, <=, >=这些比较运算符。

不过这玩意儿的高阶trait也是PartialEq,没想到吧。。。也就是说只有先实现了PartialEq才有资格实现PartialOrd

它会实现一个partial_cmp的方法,这个方法的返回值是一个Option的类型。

如果你提供的值无法进行排序,那么它会返回一个None。只要你的数据中存在一个无法比较的比如NaN,这个时候它就会返回None

当然,struct自然也是能比较的,它会比较你这俩struct的每一个字段的值。

而对于enum,则是比较这些变体的定义顺序,比如第一个变体小于最后一个变体。

搞个例子,比如rand这个crate里的 gen_range方法,它基于一个range范围来生成一个随机数。

Ord这个trait允许你注释的那个类型的两个值用于排序。它实现了cmp这个trait,返回的类型是Ordering,而不是Option

而它的高阶trait是上面仨货PartialEqEqPartialOrd

当实现的对象是struct/enum的时候,cmp这个方法的表现是和PartialOrdpartial_cmp一样的。

其中一个使用Ord的例子是BTreeSet这个数据结构,树结构自然要排序。

我们来写个例子

如果不派生PartialOrd的话是没办法使用<的,会报错。


CloneCopy用于复制值

Clone这个trait其实我们之前有说到过,在说堆和栈内存的那一章。

它允许我们深复制堆(heap)内存上的值而不是创建一个新的指针。

派生Clone会实现clone这个方法,它会应用于实现这个trait的类型的每一处。也就是说它会帮你的字段/变体/值等也实现派生这个Clone

Clone的一个例子就是切片的to_vec方法。切片大家都知道,没有数据所有权,而对于一个vector来说,它又需要元素的所有权,所以正常情况下它是无法装换成vector的,但是基于Clone这个trait,可以直接复制一份堆内存上的数据出来放到一个新的vector里面。

Copy则是相当于浅复制,它只能用于复制stack也就是栈空间的数据。你不需要去写任何的代码。

Copy这个trait没有method,因为太过重要了,所以没有任何的方法暴露出来,这样开发者就没办法去重载这个trait的方法。通过Copy来复制数据是非常快的,当然,你这个数据得是放在stack上的。

如果你的类型的所有部分都实现了Copy这个trait,那你就能派生这个trait给你的这个类型。

Copy的高阶traitClone,因为Copy里有些功能和Clone的一样。

这个trait挺少地方会用到。

并且这个trait是隐式的,你并不需要去调用clone这个方法来复制值。

我们来写个例子

我的例子没有Copy,是因为String没有实现Copy导致name这个字段没有Copy导致没法派生Copy


使用Hash把一个值转换成另一个固定大小的值

Hash这个trait允许你把任何大小的类型的实例map成一个固定大小的值,基于hash这个函数。

所以派生Hash会实现hash这个方法。由于hash这个方法会应用于这个类型的所有地方,所以所有的字段或者类型也都会跟着派生Hash

举个例子,这个trait会被用于HashMap中的K

我们来写个例子


使用Default来设置默认值

Default这个trait看名字就知道是允许你给你的类型设置一个默认值,这个看起来还挺好用的。

派生它会实现default这个方法。

同样地,它会为你的类型的每一处地方派生这个trait

它比较常见的地方是结构体的更新,如果两个结构体的类型一样的话,可以..拓展另一个结构体的数据到这个结构体内。

注意和前端的...有点区别,就是不会覆盖操作。

当我们创建一个struct实例的时候我们直接就调用..default来赋值,这样就很舒服。

另外它也被使用于unwrap_or_default这个方法中,它是Option的方法。

如果你的类型有派生这个trait的话,那他就会使用这个default方法在这个值是None的时候,当然,没有实现则直接返回一个空元组。

我们来写个例子


附录D:还用的开发工具

总所周知,工欲善其事必先利其器,好的语言离不开好的工具辅助。

rustfmt:自动格式化

貌似是自带的,如果你没有这个工具的话可以执行下面的指令

安装完之后在终端输入

然后你的代码就格式化好了。


rustfix

它是安装的时候就自带的了,然后它可以自动修复编译器警告的代码。

我们直接来试下

这段代码会被警告

i这个变量并没有被使用。

我们来运行下修复指令

然后会自动被调整为

吐槽:我终于知道_xx用来忽略无使用变量的场景了。。。。 感情是给修复工具用的。


Clippy

类似ESLint,所以是干嘛的不用多说了。

我们来安装下,貌似也是自带的。

搞个例子

这段代码咋一看没啥错误

但是当我们运行cargo clippy之后就报错了

告诉我们要使用consts::PI来代替。


rust-analyzer

这货就不多说了,如果你的IDE还没这货建议你赶紧安装。


附录E:版本

我们通过cargo new创建的项目里的Cargo.toml里的package这一栏自带了一个edition字段,它是版本的意思,我们来稍微深入了解下。

Rust和它的编译器更新迭代是六周一次(浑身是肝)。当然,更新的频率上去了更新的内容自然就少了很多,所以大多都是小版本。截止至文档编写阶段,最新版本是1.31

大概两到三年,rust开发团队会推出一个新的edition。 期间小版本的内容都会汇聚到了这个新的edition中,包括文档和工具。

我们可以通过edtion这个字段来控制当前项目rustcore版本,看情况需要使用哪个版本。

截止至文档编写时间,edtion有三个版本, Rust2015/2018/2021

初始化的项目都是2021,如果你手动去创建一个项目并且Cargo.toml没有配置edition,那么你这个项目就会使用2015的版本。

单独升级编译器的版本是不会让你的代码打包出来的东西有变化的,除非你改动edition

编译器适配任何的版本,并且你引入的crate也是适配的。

如果包的edition和项目的edtion不同又会是怎么样呢?。也是没问题的,比如2015版本的crate同样可以用于2018的项目edition,看来是向下兼容的。

另外,新特性大部分都是应用于所有的edition的,所以不用改动edition的版本也是可以享受到新特性的。

不过也有一小部分不行,比如一个新的keyword被创造出来,这个时候如果你有需要使用你就得升级版本了。

我们就理解到这里,想了解更多的话可以看这个文档

The Rust Edition Guidedoc.rust-lang.org/stable/edition-guide/


附录F:这文档的各语言翻译链接

这个就不多说了,这里就只放简体中文的

https://github.com/KaiserY/trpl-zh-cngithub.com/KaiserY/trpl-zh-cn

想要看其它版本的点下注释22跳过去即可。


附录G:Rust这门语言是如何实现的以及Nightly Rust

稳定无停滞(stability without stagnation)

作为一门语言,Rust考虑最多的点就是稳定。

Rust的开发团队希望rust的基础稳定可靠,这样开发者们使用起来就很安全了。

但是如果某些东西是一直变化着的,那这stability就几乎不可能了。

同时,如果开发团队没有先测试过新特性就发布的话,那等到发布后再想去修复就不可能了。

针对于以上两个问题,rust提出的解决方案就做stability without stagnation

它的指导理念是:不用害怕更新版本。


Choo, Choo! Release Channels and Riding the Trains

这标题我真不知道咋翻译。。。作为一个英语渣,我感到很惭愧。

Rust的开发类似操作列车时刻表,所有的开发都在master也就是主分支上完成。

发布跟随一个软件发布火车模型(train model)(目前使用这个模型的还有Cisco IOS和其它软件项目)。

rust有三种发布渠道:

  • Nightly
  • Beta
  • Stable

大部分开发者们使用的都是stable这个渠道的,如果你想尝试新特性,你可以选择nightly或者beta的渠道。

如果master分支上有人提交了代码,那么就说明有一个新特性出现了。

每一个晚上(?),都有一个nightly的版本会被发布。所以每一天都是发布日,而这些发布的版本由发布的设施自动发布。

每隔六周左右都准备发布一个非nightlybeta版本。Beta的分支会从master拉代码。

Beta是测试版本,大部分开发者都不会使用到这个渠道的版本,不过对于爱好者来说可以用来测试新特性的bug

如果有bug被发现了之后,开发团队就会补commitmaster分支,然后打补丁到beta分支上,这样beta分支就更新一次版本。

然后再过六周,我们就可以基于beta最后一次迭代代码拉到stable分支上。

那么至此,就多了一个新的稳定版本,而这个版本也是大多数开发者们使用的。

这看起来像不像火车?

每隔六周,有一趟列车从nightly发车到beta,但是还得继续往前走一段时间直到可以发布稳定版本后。


不稳定特性(unstable features)

不稳定特性也是发布模型的相关部分。

rust开发团队使用一种技术叫feature flags来决定哪些特性可以发布。如果一个特性处于活跃的开发阶段,那么它就会被添加到master分支(在feature flag的后面)。

如果你想使用这个还处于开发阶段的特性,你可以使用nightly渠道的rust版本并且在你的代码中注释这个特性flag。除此之外没有任何的渠道可以使用这个新特性。

另外,这本书中提到的所有内容都是稳定版本的内容。


rustupNightly Rust扮演的角色

默认情况我们是stable的渠道。

我们可以基于rustup快速的切换到其它渠道 ,比如切换到nightly渠道。

不过在这之前,我们需要先安装渠道。

另外你还可以使用toolchain指令列出你的电脑中的所有渠道的各个版本

可以看到我们默认是stable,如果你追逐先进的特性,那么你可以修改渠道。

注意这里是覆盖当前项目的渠道,而不是全局的。


RFC流程(Process)和团队(Teams)

RFC指的是Request For Comments也就是广纳谏。

如果你对rust有一些特别的想法,你可以到官网去发布你的想法,然后开发团队会审查讨论你这个想法。

官网有很多子团队,根据不同的功能来区分的,你可以到这里看下

https://www.rust-lang.org/governance

如果你的想法被接纳了,那就会在rust仓库里创建一个issue

然后会有人去实现它。当它被实现了之后,它就会被合并到master分支。

可能提issue和实现它的人不是同一人,但是没关系~

合并到master分支之后,这个特性就可以被标记为unstable features了。

然后那天晚上就会被发布到nightly渠道。

然后就是上面的火车模型啦~


总结

附录的内容还是挺多的,虽然只是简单的说一下。

这里面比如派生宏的其它属性,都是很有用的。

如果你有操作符忘了咋用,你可以回来这个附录里看下。

下一篇就轮到我们的最终目标--web项目实战了。

参考

  1. ^rust-Appendix https://doc.rust-lang.org/book/appendix-00.html#appendix
  2. ^rust-Appendix-A: Keywords https://doc.rust-lang.org/book/appendix-01-keywords.html#appendix-a-keywords
  3. ^rust-keyword-currently-in-use https://doc.rust-lang.org/book/appendix-01-keywords.html#keywords-currently-in-use
  4. ^rust-keyword-reserverd-for-future-used https://doc.rust-lang.org/book/appendix-01-keywords.html#keywords-reserved-for-future-use
  5. ^rust-raw-identifier https://doc.rust-lang.org/book/appendix-01-keywords.html#raw-identifiers
  6. ^rust-Appendix:B-operators-and-symbol https://doc.rust-lang.org/book/appendix-02-operators.html#appendix-b-operators-and-symbols
  7. ^rust-Operators https://doc.rust-lang.org/book/appendix-02-operators.html#operators
  8. ^rust-non-operator-symbols https://doc.rust-lang.org/book/appendix-02-operators.html#non-operator-symbols
  9. ^rust-derivable-trait https://doc.rust-lang.org/book/appendix-03-derivable-traits.html#appendix-c-derivable-traits
  10. ^rust-debug-for-programmer-output https://doc.rust-lang.org/book/appendix-03-derivable-traits.html#debug-for-programmer-output
  11. ^rust-PartialEq-and-Eq-for-Equally-comparisons https://doc.rust-lang.org/book/appendix-03-derivable-traits.html#partialeq-and-eq-for-equality-comparisons
  12. ^rust-PartialOrd-and-Ord-for-Ordering-comparisons https://doc.rust-lang.org/book/appendix-03-derivable-traits.html#partialord-and-ord-for-ordering-comparisons
  13. ^rust-Clone-and-Copy-for-duplicating-values https://doc.rust-lang.org/book/appendix-03-derivable-traits.html#clone-and-copy-for-duplicating-values
  14. ^rust-Hash-for-mapping-values-to-value-of-fixed-size https://doc.rust-lang.org/book/appendix-03-derivable-traits.html#hash-for-mapping-a-value-to-a-value-of-fixed-size
  15. ^rust-Default-to-default-values https://doc.rust-lang.org/book/appendix-03-derivable-traits.html#default-for-default-values
  16. ^rust-Appendix:D-useful-development-tools https://doc.rust-lang.org/book/appendix-04-useful-development-tools.html#appendix-d---useful-development-tools
  17. ^rust-rustfmt-automatically-formatting https://doc.rust-lang.org/book/appendix-04-useful-development-tools.html#automatic-formatting-with-rustfmt
  18. ^rust-rustfix https://doc.rust-lang.org/book/appendix-04-useful-development-tools.html#fix-your-code-with-rustfix
  19. ^rust-Clippy https://doc.rust-lang.org/book/appendix-04-useful-development-tools.html#more-lints-with-clippy
  20. ^rust-analyzer https://doc.rust-lang.org/book/appendix-04-useful-development-tools.html#ide-integration-using-rust-analyzer
  21. ^rust-Appendix:E-edtions https://doc.rust-lang.org/book/appendix-05-editions.html#appendix-e---editions
  22. ^rust-translations-of-the-book https://doc.rust-lang.org/book/appendix-06-translation.html#appendix-f-translations-of-the-book
  23. ^rust-Expendix:G-how-rust-is-made-and-Nightly-Rust https://doc.rust-lang.org/book/appendix-07-nightly-rust.html#appendix-g---how-rust-is-made-and-nightly-rust
  24. ^rust-stability-and-stagnation https://doc.rust-lang.org/book/appendix-07-nightly-rust.html#stability-without-stagnation
  25. ^Choo, Choo! Release Channels and Riding the Trains https://doc.rust-lang.org/book/appendix-07-nightly-rust.html#choo-choo-release-channels-and-riding-the-trains
  26. ^rust-unstable-features https://doc.rust-lang.org/book/appendix-07-nightly-rust.html#unstable-features
  27. ^rustup-and-role-of-rust-nightly https://doc.rust-lang.org/book/appendix-07-nightly-rust.html#rustup-and-the-role-of-rust-nightly
  28. ^rust-process-and-teams https://doc.rust-lang.org/book/appendix-07-nightly-rust.html#the-rfc-process-and-teams

发布于 2023-01-13 12:21・IP 属地广东