Tip #21
在 Rust 中,我们经常使用 Clone()
或 Copy()
。这两者之间的区别是什么?
Copy
:支持Copy
的类型可以安全地通过字节复制的方式进行复制,可以类比 C 语言中的memcpy
函数。Clone
:支持Clone
的类型也可以被复制,但它通常需要执行一些逻辑操作来完成深拷贝。
Tip #22
我之前忽略的一点是,Rust 中有 static
变量,可以用来追踪某些状态。当然, 可变的静态变量 (mutable static)是不支持的,但对于原始类型,可以考虑使用std::sync::atomic。这些可以被实例化为静态的,并且在后续可以被修改:
|
|
Tip #23
对于大多数使用核心数据类型的结构体,你可以通过派生 Default
trait自动生成一个基本的Default()实现:
|
|
Tip #24
探索智能指针:
Box<T>
用于独占所有权,一旦它所在的作用域{}
结束,它就会被释放。Rc<T>
是一种引用计数的智能指针。只有当它的所有引用都不存在时,它才会被释放。Arc<T>
是Rc<T>
的线程安全版本。
Tip #25
在Rust中,trait的工作方式类似于其他语言中的接口定义。实现某个trait的结构体或枚举,在契约上必须提供trait中指定签名的函数 :
|
|
Tip #26
你知道Rust支持对大多数常见数据类型进行解构吗?这里有一个关于结构体的例子:
|
|
这种解构方式允许你在一行中从结构体中提取多个字段,并给它们起新的名字或指定类型,这在处理复杂数据时非常有用。
Tip #27
Rust #区间表达式:
- 包含区间(包含a到b,b也包括在内):
a..=b
- 半开区间(包含a到b-1):
a..b
- 从a开始:
a..
- 到b-1为止:
..b
- 到b为止(包括b):
..=b
- 完整区间:
..
Tip #28
区间表达式(继续):
区间表达式可以应用于for循环,或用于创建迭代器。别忘了调用collect()来实际执行迭代器:
|
|
Tip #29
迭代器可以通过 chain()
方法进行连续拼接。Rust 在处理可能含有或不含值的 Option
类型的连续操作时表现得尤为优雅。
|
|
Tip #30
如果需要以非可变方式将向量(vector
)传递给函数,你可以使用 &[T]
(等同于 &Vec<T>
)类型的参数,这也就是所谓的 切片
(slice
)。
切片的优势包括:它们避免了所有权的转移,并且对于 #并发或 #并行操作是安全的。
Tip #31
动态调度(dynamic dispatch
)简单来说,是在程序运行时动态地处理不同类型的特性,通过一个公共的特质(trait)来实现,从而使得(具有 Rust 特色的)多态成为可能。
在 Rust 中,Box<dyn Trait>
通常表明使用了动态调度。
Tip #32
迭代器提供了一些非常方便的实用功能。其中之一是 all()
方法,它会检查迭代器中所有元素是否都满足给定的条件。
这使我们能够以优雅且符合习惯用法的方式重写难看的基于for循环的代码:
|
|
Tip #33
let a: Arc<Mutex<Vec<f32>>>
这样的声明在视觉上是否让你觉得困扰?这时可以使用 type
关键字来定义类型 别名(alias):
|
|
这样,你就可以使用 SharedSafeVec<f32>
来代替 Arc<Mutex<Vec<f32>>>
。
Tip #34
Option<T>.map()
是一种将选项(Option
)从一种类型转换为另一种类型的极佳方式。它能透明地处理 None
值的情况。
请看以下示例,我们将 Option<u32>
转换为 Option<String>
:
|
|
Tip #35
什么是 trait bound
? 当我们向带有泛型参数的函数中传递额外的trait名称,以便限制该泛型类型时,就是在谈论trait bound
:
|
|
你可以使用 "+" 运算符来组合多个特质。这样一来,类型 T 就需要同时满足 TraitA
和 TraitB
这两个特质的要求。
Tip #36
如需从应用程序获取更详细的日志输出,尝试导出环境变量 RUST_LOG={error, warn, info, debug, trace}
。
以下是一个使用 actix-web
运行的服务在 trace
模式下的示例,它会提供超级详尽的日志输出:
![[Pasted image 20240608183308.png]]
Tip #37
元组结构体对于封装值并附加可通过 Rust 的类型系统验证的元数据非常有用。
元组结构体的一个妙用是模拟计量单位——这样就不会再混淆英尺和米了:
|
|
Tip #38
正在编写一个函数但还没准备好最终完成?可以使用 todo!()
或 unimplemented!()
宏来让代码保持可编译状态。但要记住,如果你的程序运行时遇到这些点,它将会panic
!这对于开发阶段非常理想。🚧
和第四条重复了
Tip #39
30秒速成指南:构建 Rust #模块
创建你的模块结构:
|
|
在 mod.rs 中添加:
|
|
或者新的方式:
|
|
🌟 小贴士:使用pub
关键字来定义公有访问权限。
Tip #40
实际上,Rust 中有两种类型的宏。声明式宏
(declarative)和更高级的 过程式宏
(procedural)。下面是一个使用 macro_rules!
宏来生成 println
功能的声明式宏示例:
|
|
这段代码定义了一个名为 my_println!
的宏,它接收任意数量的参数并简单地将它们传递给标准库的 println!
宏,从而达到打印输出的目的。这是声明式宏的一个基本应用,它们基于规则匹配并在编译时展开。