Cheatsheet
// take 相当于
mem::replace(&mut option, None)
// map(fn) 相当于
match option {
None => None,
Some(x) => Some(fn(x))
}
// and_then(fn) 相当于
match option {
None => None,
Some(x) => fn(x)
}
// unwrap_or(default) 相当于
match option {
None => default,
Some(x) => x
}
// ok() 相当于
match result {
Ok(x) => x,
Err => None
}
fn main() {
let array = [1,2,3];
// 编译错误,array 没有实现 Iterator 特性
// for i in array {
// println!("{}", i);
// }
for i in &array {
println!("{}", i);
}
println!("{:?}", array.iter().map(|&x| x * 2).collect::<Vec<_>>());
// https://doc.rust-lang.org/std/primitive.array.html
// IntoIterator (implemented for &[T; N] and &mut [T; N])
// 由于只对数组引用实现了 IntoIterator,在调用 into_iter 时自动将 array 转换成 &array
println!("{:?}", array.into_iter().map(|&x| x * 2).collect::<Vec<_>>());
let vector = vec![1,2,3];
// 向量内元素被移出,无法再次使用
// for i in vector {
// println!("{}", i);
// }
for i in &vector {
println!("{}", i);
}
// https://doc.rust-lang.org/std/iter/#the-three-forms-of-iteration
// iter 遍历 &T,iter_mut 遍历 &mut T, into_iter 遍历 T
// 关键:iter 不移出元素,into_iter 移出元素
println!("{:?}", vector.iter().map(|&x| x * 2).collect::<Vec<_>>());
// into_iter 之后 vector 无法再使用,此处 x 为值而非引用
println!("{:?}", vector.into_iter().map(|x| x * 2).collect::<Vec<_>>());
}
基本概念
基本类型
// 元组
let x: (i32, f64, u8) = (500, 6.4, 1);
let five_hundred = x.0;
let six_point_four = x.1;
let one = x.2;
// 数组
let a: [i32; 5] = [1, 2, 3, 4, 5];
let first = a[0];
控制语句
// 条件语句
let number = if condition { 5 } else { 6 };
// 循环语句
// 1. loop
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2;
}
};
// 2. while
let mut number = 5;
while number != 0 {
println!("{}!", number);
number = number - 1;
}
// 3. for
let a = [10, 20, 30, 40, 50];
for element in a.iter() {
println!("the value is: {}", element);
}
for number in (1..4).rev() {
println!("{}!", number);
}
结构体
struct Point<T> {
x: T,
y: T,
}
// 结构体泛型方法
impl<T> Point<T> {
fn x(&self) -> &T {
&self.x
}
}
// 结构体特定类型方法
impl Point<f32> {
fn distance_from_origin(&self) -> f32 {
(self.x.powi(2) + self.y.powi(2)).sqrt()
}
}
// 打印结构体
##[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
fn main() {
let rect1 = Rectangle { width: 30, height: 50 };
// {} 默认使用 Display 输出格式,{:?} 使用 Debug 输出格式
println!("rect1 is {:?}", rect1);
}
枚举类
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
// 定义枚举类方法
impl Message {
fn call(&self) {
// 在这里定义方法体
}
}
模式匹配
let some_u8_value = Some(0u8);
match some_u8_value {
Some(3) => println!("three"),
_ => (),
}
// 语法糖
if let Some(3) = some_u8_value {
println!("three");
}
通用类型
向量
// 新建向量
let v: Vec<i32> = Vec::new(); // 空向量
let v = vec![1, 2, 3]; // 利用宏
let v = vec![String::new(); 5]; // 设置元素个数
let mut v = Vec::new(); // 更新元素
v.push(5);
// 获取向量元素
let v = vec![1, 2, 3, 4, 5];
let third: &i32 = &v[2]; // 使用索引,超出范围引发 panic
match v.get(2) { // 使用get方法,超出范围返回 None
Some(third) => println!("The third element is {}", third),
None => println!("There is no third element."),
}
// 遍历向量元素
let v = vec![100, 32, 57];
for i in &v {
println!("{}", i);
}
let mut v = vec![100, 32, 57];
for i in &mut v {
*i += 50; // 解引用运算符(*)
}
字符串
// 新建字符串
let s = String::from("initial contents");
let s = "initial contents".to_string();
// 更新字符串
let mut s = String::from("foo");
s.push_str("bar");
s.push('l');
// 拼接字符串
// +运算符
let s1 = String::from("Hello, ");
let s2 = String::from("world!");
let s3 = s1 + &s2; // 注意 s1 被移动了,不能继续使用
// format!宏
let s1 = String::from("tic");
let s2 = String::from("tac");
let s3 = String::from("toe");
let s = format!("{}-{}-{}", s1, s2, s3);
// 遍历字符串
for c in "नमस्ते".chars() {
println!("{}", c);
}
字符串不支持索引!
哈希表(字典)
use std::collections::HashMap;
// 新建哈希表
// 1. 用 insert 方法
let mut scores = HashMap::new();
let team = String::from("Blue");
let score = 10;
scores.insert(team, score);
// 一旦键值对被插入后就为哈希表所有,以下 team, score 不再有效
// 2. 用 collect 方法
let teams = vec![String::from("Blue"), String::from("Yellow")];
let initial_scores = vec![10, 50];
// 用下划线占位,rust能够推断出类型
let scores: HashMap<_, _> = teams.iter().zip(initial_scores.iter()).collect();
// 访问哈希表中的值
let team_name = String::from("Blue");
// 1. 通过键值访问
let score = scores[&team_name] // 键值不存在引发 panic
let score = scores.get(&team_name); // 键值不存在返回 None
// 2. 遍历
for (key, value) in &scores {
println!("{}: {}", key, value);
}
// 更新哈希表
// 覆盖值
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Blue"), 25);
// 只在没有键时插入
scores.entry(String::from("Blue")).or_insert(50);
错误处理
匹配错误
use std::fs::File;
use std::io::ErrorKind;
fn main() {
// 直接使用模式匹配
let f = File::open("hello.txt");
let f = match f {
Ok(file) => file,
Err(error) => match error.kind() {
ErrorKind::NotFound => match File::create("hello.txt") {
Ok(fc) => fc,
Err(e) => panic!("Tried to create file but there was a problem: {:?}", e),
},
other_error => panic!("There was a problem opening the file: {:?}", other_error),
},
};
// 简写
let f = File::open("hello.txt").map_err(|error| {
if error.kind() == ErrorKind::NotFound {
// 用 unwrap_or_else 处理错误
File::create("hello.txt").unwrap_or_else(|error| {
panic!("Tried to create file but there was a problem: {:?}", error);
})
// 或者用 if let 语句处理
if let Err(e) = File::create("hello.txt") {
panic!("Tried to create file but there was a problem: {:?}", e);
}
} else {
panic!("There was a problem opening the file: {:?}", error);
}
});
}
// 简写失败时的panic
// unwrap 使用默认 panic! 信息
let f = File::open("hello.txt").unwrap();
// expect 手动设定错误信息
let f = File::open("hello.txt").expect("Failed to open hello.txt");
传播错误
// ? 只能用于返回 Result 的方法
// ? 将错误传递给 from 方法(定义于 From trait 中),from 方法负责将错误转换为当前方法返回值中定义的错误类型
fn read_username_from_file() -> Result<String, io::Error> {
// 示例
let mut f = File::open("hello.txt")?;
let mut s = String::new();
f.read_to_string(&mut s)?;
Ok(s)
// 简写
let mut s = String::new();
File::open("hello.txt")?.read_to_string(&mut s)?;
Ok(s)
// 更简
fs::read_to_string("hello.txt")
}
模块系统
定义模块
// 所有项默认私有,不能使用定义于当前模块的子模块中的私有代码,除非用pub公有化
mod sound {
pub mod instrument {
pub fn clarinet() {
// 函数体
}
}
}
对结构体和枚举使用pub
// 公有结构体的成员仍是私有,必须手动公开
pub struct Vegetable {
pub name: String,
id: i32,
}
// 公有枚举所有成员都公有
pub enum Appetizer {
Soup,
Salad,
}
相对路径与绝对路径
// main 与 sound 同级别
fn main() {
// 绝对路径
crate::sound::instrument::clarinet();
// 相对路径
sound::instrument::clarinet();
}
// 使用 super 开始相对路径
mod instrument {
fn clarinet() {
// 类似于文件系统中的..
super::breathe_in();
}
}
fn breathe_in() {
// 函数体
}
引入作用域
use std::fmt::Result;
use std::io::Result as IoResult;
// 嵌套路径
use std::{cmp::Ordering, io};
use std::fs::{self, File};
// glob 运算符
use std::collections::*;
特性
定义特性
pub trait Summary {
fn summarize(&self) -> String;
}
定义带默认实现的特性
pub trait Summary {
fn summarize(&self) -> String {
String::from("(Read more...)")
}
}
// 带默认实现的方法可以调用未实现方法
pub trait Summary {
fn summarize_author(&self) -> String;
fn summarize(&self) -> String {
format!("(Read more from {}...)", self.summarize_author())
}
}
为结构体实现特性
pub struct Tweet {
pub username: String,
pub content: String,
pub reply: bool,
pub retweet: bool,
}
impl Summary for Tweet {
fn summarize(&self) -> String {
format!("{}: {}", self.username, self.content)
}
}
特性约束(trait bound)
pub fn notify<T: Summary>(item: T) { }
pub fn notify<T: Summary + Display>(item: T) { }
// 语法糖
pub fn notify(item: impl Summary) { }
pub fn notify(item: impl Summary + Display) { }
多个特性约束(trait bound)
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
{ }
生命周期
// 返回值的生命周期和x,y之间生命周期更短的那个一样
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
// 指定结构体的实例的生命周期不能长于part字段中的引用
struct ImportantExcerpt<'a> {
part: &'a str,
}
impl<'a> ImportantExcerpt<'a> {
fn announce_and_return_part(&self, announcement: &str) -> &str {
println!("Attention please: {}", announcement);
self.part
}
}
// 静态生命周期,存活于整个程序期间
// 字符串字面值的类型是 &'static str
let s: &'static str = "I have a static lifetime.";
测试
单元测试
// 单元测试混杂在源代码中,通过 cfg(test) 告诉编译器在编译时不需要把他们包含进编译结果中
##[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}
集成测试
新建
tests
文件夹,在其中添加测试文件如tests/integration_test.rs
use crate_name;
// 由于集成测试与源代码分离,不需要 cfg(test) 标注
##[test]
fn it_adds_two() {
assert_eq!(4, adder::add_two(2));
}
如果多个集成测试需要共用某些方法,可以创建
tests/common/mod.rs
而非tests/common.rs
,这样编译器会把它当作模块而不是测试文件
命令行程序
// 读取命令行参数
use std::env;
let args: Vec<String> = env::args().collect();
// 检查环境变量
// env::var 方法返回 Result,is_err 判断返回值是否为 Err
let case_sensitive = env::var("CASE_INSENSITIVE").is_err();
// 将错误打印到标准错误
eprintln!("Application error: {}", e);