pull/95/head
sunface 3 years ago
parent 7d9e2e042f
commit 3bd0dd84c6

@ -60,10 +60,13 @@
- [函数式编程(todo)](advance/functional-programing.md)
- [智能指针(todo)](advance/smart-pointer.md)
- [全局变量](advance/global-variable.md)
- [一些写代码的技巧](advance/coding-tips.md)
## 专题内容,每个专题都配套一个小型项目进行实践
- [Rust最佳实践 todo](practice/intro.md)
- [一些写代码的技巧 todo](practice/coding-tips.md)
- [最佳实践 todo](practice/best-pratice.md)
- [错误处理 todo](errors/intro.md)
- [简化错误处理](errors/simplify.md)
- [自定义错误](errors/user-define.md)
@ -162,6 +165,7 @@
- [幽灵数据(todo)](fight-with-compiler/phantom-data.md)
- [生命周期)](fight-with-compiler/lifetime/intro.md)
- [生命周期过大-01](fight-with-compiler/lifetime/too-long1.md)
- [生命周期过大-02](fight-with-compiler/lifetime/too-long2.md)
- [类型未限制(todo)](fight-with-compiler/unconstrained.md)
- [宏编程 todo](macro/intro.md)

@ -113,6 +113,7 @@ Rust 每 6 周发布一个版本,如时钟般准确。如果你知道了某个
### Rustup 和 Rust Nightly 的职责
#### 安装Rust Nightly版本
Rustup 使得改变不同发布通道的 Rust 更为简单,其在全局或分项目的层次工作。其默认会安装稳定版 Rust。例如为了安装 nightly
```text
@ -128,6 +129,7 @@ beta-x86_64-pc-windows-msvc
nightly-x86_64-pc-windows-msvc
```
#### 在指定目录使用Rust Nightly
如你所见,默认是稳定版。大部分 Rust 用户在大部分时间使用稳定版。你可能也会这么做,不过如果你关心最新的功能,可以为特定项目使用 nightly 版。为此,可以在项目目录使用 `rustup override` 来设置当前目录 `rustup` 使用 nightly 工具链:
```text

@ -217,6 +217,13 @@ fn main() {
```
因为`String`类型没有实现`Draw`特征,编译器直接就会报错,不会让上述代码运行。如果想要`String`类型被渲染在屏幕上,那么只需要为其实现`Draw`特征即可,非常容易。
#### &和dyn的区别
前文提到,`&`和`dyn`都可以用于特征对象,因此在功能上`&`和`dyn`几无区别,唯一的区别就是:`&`减少了一次指针调用。
因为`dyn`是一个宽指针(`fat pointer`), 它内部保存一个指针指向`vtable`,然后通过`vtable`查询到具体的函数指针,最后进行调用.
所以,如果你在乎性能,又想使用特征对象简化代码,可以优先考虑`&`。
## 特征对象的动态分发
回一下泛型章节我们提到过的,泛型是在编译期完成处理的:编译器会为每一个泛型参数对应的具体类型生成一份代码,这种方式是**静态分发(static dispatch)**,因为是在编译期完成的,对于运行期性能完全没有任何影响。

@ -0,0 +1,169 @@
# 生命周期过大-02
继上篇文章后,我们再来看一段**可能**涉及生命周期过大导致的无法编译问题:
```rust
fn bar(writer: &mut Writer) {
baz(writer.indent());
writer.write("world");
}
fn baz(writer: &mut Writer) {
writer.write("hello");
}
pub struct Writer<'a> {
target: &'a mut String,
indent: usize,
}
impl<'a> Writer<'a> {
fn indent(&'a mut self) -> &'a mut Self {
&mut Self {
target: self.target,
indent: self.indent + 1,
}
}
fn write(&mut self, s: &str) {
for _ in 0..self.indent {
self.target.push(' ');
}
self.target.push_str(s);
self.target.push('\n');
}
}
fn main() {}
```
报错如下:
```console
error[E0623]: lifetime mismatch
--> src/main.rs:2:16
|
1 | fn bar(writer: &mut Writer) {
| -----------
| |
| these two types are declared with different lifetimes...
2 | baz(writer.indent());
| ^^^^^^ ...but data from `writer` flows into `writer` here
```
WTF这什么报错之前都没有见过而且很难理解什么叫`writer`滑入了另一个`writer`
别急,我们先来仔细看下代码,注意这一段:
```rust
impl<'a> Writer<'a> {
fn indent(&'a mut self) -> &'a mut Self {
&mut Self {
target: self.target,
indent: self.indent + 1,
}
}
```
这里的生命周期定义说明`indent`方法使用的。。。等等!你的代码错了,你怎么能在一个函数中返回一个新创建实例的引用?!!最重要的是,编译器不提示这个错误,竟然提示一个莫名其妙看不懂的东东。
行,那我们先解决这个问题,将该方法修改为:
```rust
fn indent(&'a mut self) -> Writer<'a> {
Writer {
target: self.target,
indent: self.indent + 1,
}
}
```
怀着惴惴这心,再一次运行程序,果不其然,编译器又朝我们扔了一坨错误:
```console
error[E0308]: mismatched types
--> src/main.rs:2:9
|
2 | baz(writer.indent());
| ^^^^^^^^^^^^^^^
| |
| expected `&mut Writer<'_>`, found struct `Writer`
| help: consider mutably borrowing here: `&mut writer.indent()`
```
哦,这次错误很明显,因为`baz`需要`&mut Writer`,但是咱们`writer.indent`返回了一个`Writer`,因此修改下即可:
```rust
fn bar(writer: &mut Writer) {
baz(&mut writer.indent());
writer.write("world");
}
```
这次总该成功了吧?再次心慌慌的运行编译器,哐:
```console
error[E0623]: lifetime mismatch
--> src/main.rs:2:21
|
1 | fn bar(writer: &mut Writer) {
| -----------
| |
| these two types are declared with different lifetimes...
2 | baz(&mut writer.indent());
| ^^^^^^ ...but data from `writer` flows into `writer` here
```
可恶,还是这个看不懂的错误,仔细检查了下代码,这次真的没有其他错误了,只能硬着头皮上。
大概的意思可以分析,生命周期范围不匹配,说明一个大一个小,然后一个`writer`中流入到另一个`writer`说明,两个`writer`的生命周期定义错了,既然这里提到了`indent`方法调用,那么我们再去仔细看一眼:
```rust
impl<'a> Writer<'a> {
fn indent(&'a mut self) -> Writer<'a> {
Writer {
target: self.target,
indent: self.indent + 1,
}
}
...
}
```
好像有点问题,`indent`返回的`Writer`的生命周期和外面的`Writer`的生命周期一模一样,这很不合理,一眼就能看出前者远小于后者,那我们尝试着修改下`indent`:
```rust
fn indent<'b>(&'b mut self) -> Writer<'b> {
Writer {
target: self.target,
indent: self.indent + 1,
}
}
```
Bang! 编译成功,不过稍等,回想下生命周期消除的规则,我们还可以实现的更优雅:
```rust
fn bar(writer: &mut Writer) {
baz(&mut writer.indent());
writer.write("world");
}
fn baz(writer: &mut Writer) {
writer.write("hello");
}
pub struct Writer<'a> {
target: &'a mut String,
indent: usize,
}
impl<'a> Writer<'a> {
fn indent(&mut self) -> Writer {
Writer {
target: self.target,
indent: self.indent + 1,
}
}
fn write(&mut self, s: &str) {
for _ in 0..self.indent {
self.target.push(' ');
}
self.target.push_str(s);
self.target.push('\n');
}
}
fn main() {}
```
至此,问题彻底解决,太好了,我感觉我又变强了。可是默默看了眼自己的头发,只能以`哎~`一声叹息结束本章内容。

@ -0,0 +1,3 @@
# 最佳实践
https://www.reddit.com/r/rust/comments/rgjsbt/whats_your_top_rust_tip_crate_tool_other_for/

@ -0,0 +1 @@
# Rust最佳实践

@ -1 +1,62 @@
# 性能测试
## benchmark迷一般的性能结果
代码如下
```rust
#![feature(test)]
extern crate test;
fn fibonacci_u64(number: u64) -> u64 {
let mut last: u64 = 1;
let mut current: u64 = 0;
let mut buffer: u64;
let mut position: u64 = 1;
return loop {
if position == number {
break current;
}
buffer = last;
last = current;
current = buffer + current;
position += 1;
};
}
#[cfg(test)]
mod tests {
use super::*;
use test::Bencher;
#[test]
fn it_works() {
assert_eq!(fibonacci_u64(1), 0);
assert_eq!(fibonacci_u64(2), 1);
assert_eq!(fibonacci_u64(12), 89);
assert_eq!(fibonacci_u64(30), 514229);
}
#[bench]
fn bench_u64(b: &mut Bencher) {
b.iter(|| {
for i in 100..200 {
fibonacci_u64(i);
}
});
}
}
```
通过`cargo bench`运行后,得到一个难以置信的结果:`test tests::bench_u64 ... bench: 0 ns/iter (+/- 0)`, 难道Rust已经到达量子计算机级别了
其实,原因藏在`LLVM`中: `LLVM`认为`fibonacci_u64`函数调用的结果没有使用,同时也认为该函数没有任何副作用(造成其它的影响,例如修改外部变量、访问网络等), 因此它有理由把这个函数调用优化掉!
解决很简单使用Rust标准库中的[`black_box`](https://doc.rust-lang.org/std/hint/fn.black_box.html)函数:
```rust
for i in 100..200 {
black_box(fibonacci_u64(black_box(i)));
}
```
通过这个函数告诉编译器尽量少的做优化此时LLVM就不会再自作主张了:)
Loading…
Cancel
Save