Rust 基础
基本概念
内存所有权
变量在离开作用域后就被自动释放 将 String 赋值给其它变量,意味着从栈上拷贝了它的指针、长度和容量,并没有复制指针指向的堆上数据。 Rust 的操作方式称为移动而不是浅拷贝。
对于只在栈上的数据进行拷贝。如果一个类型实现了 Copy trait,在旧的变量赋值给其它变量后仍然可用。
向函数传递值,会使变量进入函数的作用域,在离开作用域时占用的内存会被释放。 函数的返回值也会转移所有权,将变量的值移出给调用的函数。
引用与借用
将对象的引用作为参数,而不是获取值的所有权。
引用默认不可变,使用可变引用 &mut,对一个变量同时最多只能有一个可变引用;拥有不可变引用时不可创建可变引用。(重叠作用域)
切片类型
引用集合中一段连续的元素序列,而不用引用整个集合。
fn first_word(s: &String) -> &str {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[0..i];
}
}
&s[..]
}
泛型
Traits
定义特定类型可能与其它类型共享的功能。 类似于接口。
pub fn notify(item1: impl Summary, item2: impl Summary) {}
pub fn notify<T: Summary>(item1: T, item2: T) {}
通过 where 简化
fn some_function<T: Display + Clone, U: Clone + Debug>(t: T, u: U) -> i32 {}
fn some_function<T, U>(t: T, u: U) -> i32
where T: Display + Clone,
U: Clone + Debug
{}
生命周期
数据比引用有更长的生命周期,则是有效的引用。
生命周期标注表示多个引用的泛型生命周期参数如何相互关系。 不满足约束的值将被借用检查器拒绝。
#![allow(unused)]
fn main() {
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
}
// 返回值的生命周期必须与 x, y 一致
静态生命周期,能够存活于整个程序期间。
闭包
惰性求值,只在需要结果时执行闭包,并会缓存结果值
struct Cacher<T>
where T: Fn(u32) -> u32
{
calculation: T,
value: Option<u32>,
}
impl<T> Cacher<T>
where T: Fn(u32) -> u32
{
fn new(calculation: T) -> Cacher<T> {
Cacher {
calculation,
value: None,
}
}
fn value(&mut self, arg: u32) -> u32 {
match self.value {
Some(v) => v,
None => {
let v = (self.calculation)(arg);
self.value = Some(v);
v
},
}
}
}
// 改用 HashMap 以支持不同的 arg
智能指针
Treating Smart Pointers Like Regular References with the Deref Trait
引用计数,允许数据有多个所有者,记录总共由多少个所有者,当没有所有者时清理数据。智能指针相比引用,拥有它们指向的数据。
智能指针实现了 Deref 和 Drop 。Deref 使实例表现像引用一样,Drop 可以自定义离开作用域时的代码。
Box<T>用于在堆上分配值Rc<T>引用计数类型,其数据可有多个所有者Ref<T>和RefMut<T>,只读引用和可变引用
Deref 允许重载解引用运算符 *
Drop 运行清理代码
Rc<T> 通过不可变引用,在程序的多个部分之间只读地共享数据。
use std::rc::Rc;
let a = Rc::new();
let b = Rc::clone(&a);
let c = Rc::clone(&a);
println!("{}", Rc::strong_count(&a));
Rc::clone 只会增加引用计数,不会进行深拷贝。
RefCell<T> 能够在外部值被认为是不可变的情况下修改内部值
use std::cell::RefCell;
struct MockMessenger {
sent_messages: RefCell<Vec<String>>,
}
impl MockMessagenger {
fn new() -> MockMessenger {
MockMessenger { sent_messages: RefCell::new(vec![])}
}
}
impl Messenger for MockMessenger {
fn send(&self, message: &str) {
self.sent_messages.borrow_mut().push(String::from(message));
}
}
避免引用循环:将 Rc<T> 变为 Weak<T>
调用 Rc::downgrade 会得到 Weak<T> 类型的智能指针。
弱引用并不属于所有权关系,弱引用的循环会在强引用计数为 0 时被打断。
为了使用指向的值,调用 Weak<T> 的 upgrade 方法,返回 Option<Rc<T>>
异步
Rust Futures > Async Rust: What is a runtime? Here is how tokio works under the hood
Rust 并不提供用于执行 Futures 和 Streams 的上下文。如果不进行嵌入式开发,选择 tokio 即可。
异步的核心是 event loops 或称作 processors 每个 event-loop 有自己的任务队列,tokio 中的 processor 如果任务队列为空可以从其它 processor 处抢夺。
tokio::spawn(async move{
for port in MOST_COMMON_PORTS_100 {
let _ = input_tx.send(*port).await;
}
});
可能导致阻塞的耗时任务 tokio::task::spawn_blocking
(后台的无限循环任务应该使用 Thread)
系统编程
测试
单元测试
使用 #[cfg(test)] 标注只在 cargo test 才编译运行测试代码
pub fn add_two(a: i32) -> i32 {
a + 2
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
assert_eq!(4, add_two(2));
}
}
集成测试
在与 src 同级的 tests 文件夹下,每一个文件当作独立的 crate 来编译
通过指定测试函数的名称作为 cargo test 的参数来运行特定集成测试
也可以指定运行 tests 目录下 integration_test.rs 中的所有测试
cargo test --test integration_test