Tip #001
Rust 不支持静态 vec(static vec
),但是最接近的是静态数组。例如,如果你想存储三个字符串的数组,可以尝试这样: static STRINGS : [&str;3] = ["a", "b", "c"]
Tip #002
什么是可选值(optional
)和 unwrap()
? 可以将可选值想象成一个信封,它可以包含一个值(Some(item)
)或者什么都没有(None
)。对可选值调用 unwrap()
要么返回包含的值,要么如果可选值是 None
的话就会使程序panic。
Tip #003
关于可选值( optional
)的安全解包方式:
- 使用
match
语句明确处理不同情况
unwrap_or_default
: 要么解包得到值,要么返回默认值
unwrap_or_else
: 允许你指定一个函数来处理 None/Error
解包结果
Tip #004
如果你没有时间完成特定的一段代码,但仍然希望程序可以编译,可以考虑使用 todo!()
或 unimplemented!()
宏。你的代码会继续编译通过,但如果程序运行到包含这些宏的代码块中,它将会panic。
todo!
更适合临时标记,而 unimplemented!
则更适合长期未实现的情况。
Tip #005
如果你想测试一个枚举类型的实例是否符合枚举的特定变体,你可以使用 matches!
宏,例如:
let match_res = matches!(my_variable, enum_type);
你也可以匹配其他模式,如范围,例如: matches!(foo, 'A'..='Z')
Tip #006
你知道吗, {}
块可以像函数一样返回结果?这使得基于条件的赋值变得非常容易。例如:
1 2 3 4 5 6 7
| let car_ready = { start_engine(); match engine_state { Engine::running => true, Engine::error => false } }
|
Tip #007
const和static之间有什么区别?
- const值在编译期间会被替换("内联")到你的代码中。这对于在代码的各个地方使用的常量值来说是理想的。
- static值在运行时有一个内存地址,并且会在整个应用程序的生命周期中存在。它们可以是可变的。如果你需要一个固定的位置来存放共享资源,例如硬件驱动程序或数据库接口,那么静态变量就很有用。
Tip #008
如果你想将几个相同或不同类型的值一起存储,元组类型就很有用。下面是一个将元组类型声明为结构体元组的示例,访问元组类型字段,以及从函数返回"匿名"元组类型:
1 2 3 4 5 6
| struct MyTuple(String, i32); fn flip(a: (i32, String)) -> MyTuple { let my_tuple = MyTuple(String::from("the answer"), 42); (a.1, a.0) }
|
Tips #009
让我们来谈谈 string
在 Rust 中有两种基本的字符串类型: String
和 str
。
String
: (也称为 Owned String
),在堆上分配内存并且可变。String
在运行时使用,当你想要创建和修改字符串时。你可以将 String
作为 &str
引用传递给只读函数。
str
: (也称为 String Slice
) 是对一序列 UTF8
数据的引用。你可以在编译时以常量、静态字面量的形式创建 str
,或者在运行时从 String
对象获取它们。str
总是不可变的。
Tips #010
拼接 string
有两种方式可以将两个字符串连接,分别使用:
1 2 3 4 5 6 7 8 9
| let mut first = String::from("Hello, "); first.push_str("world!"); assert_eq!(first, "Hello, world!"); let second = String::from("Hello, "); let combined = format!("{}{}", second, "world!"); assert_eq!(combined, "Hello, world!");
|
Tips #011
格式化打印宏
print!
、println!
: 在控制台打印文本。
您可以使用格式字符串打印变量的内容,例如, let a: i32 = 1234; println!("The value of a is: {}", a);
将打印出 "The value of a is 1234"
eprintln!
: 打印到标准错误流(stderr
)
dbg!
: 打印变量的值和行号,对于轻量级调试很有用。
Tips #012
可选值解包使用 if let()
类似于 Swift
, Rust 允许我们使用 if let
来测试一个可选值是否有值,这种方式便于保持程序流程的简洁:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| fn main() { let optional_value: Option<i32> = Some(9); match optional_value { Some(value) => println!("The value is: {}", value), None => println!("There is no value"), } if let Some(value) = optional_value { println!("The value is: {}", value); } else { println!("There is no value"); } }
|
Tips #013
使用 if let
解包多个可选值。继上一条 tip,如果你需要同时检查多个可选值是否都有值,你可以使用 if let
来测试和解包可选值的元组:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| fn main() { let optional_tuple: (Option<i32>, Option<bool>) = (Some(5), Some(true)); match optional_tuple { (Some(int_value), Some(bool_value)) => { println!("Received int: {} and bool: {}", int_value, bool_value); } _ => println!("One or more options were None"), } if let (Some(int_value), Some(bool_value)) = optional_tuple { println!("Received int: {} and bool: {}", int_value, bool_value); } else { println!("One or more options were None"); } }
|
Tips #014
实例化 vector
。
Vec是一种动态数组类型,非常适合存储相同数据类型的序列。下面是一些使用标准库调用和vec!宏实例化Vec的方式。注意类型是如何处理的:
1 2 3 4 5 6 7 8 9 10 11 12
| let mut a: Vec<i64> = Vec::new(); a.push(1_i64); let mut b: Vec<i64> = Vec::<i64>::new(); let mut c: Vec<i64> = vec![]; let mut d: Vec<i64> = vec![1_i64, 2_i64, 3_i64];
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| let mut vec1: Vec<i32> = Vec::new(); let mut vec2 = vec![1, 2, 3]; let mut vec3 = vec![0; 5]; let vec4 = (0..5).collect(); let vec5: Vec<_> = ["foo", "bar"].into_iter().collect(); let mut vec6 = Vec::with_capacity(10);
|
Tip #015
Vector迭代。
你可以使用 vec.iter()
来对 Vector进行迭代,使用 map()
或 for_each()
函数,这与例如 JavaScript 中的方式类似。
1 2 3 4 5 6 7 8 9 10 11
| let numbers: Vec<i32> = vec![1, 2, 3, 4, 5]; numbers.iter().for_each(|&x: i32| { println!("{}", x); }); let squared: Vec<i32> = numbers.iter().map(|&x: i32| x * x).collect(); println!("{:?}", squared);
|
Tips #016
嘿,你有一些不错的HashMap
,怎么初始化它们?除了创建一个新的HashMap
并插入键值对之外,一种值得注意的方式是使用一个元组数组和一个#迭代器:
1 2 3 4 5 6 7 8 9 10 11 12
| use std::collections::HashMap; fn main() { let codes: HashMap<_, _> = [ (101, "abc"), (102, "def"), (103, "ghi"), ].iter().cloned().collect(); println!("codes = {:?}", codes); }
|
Tips #017
更好的方式来unwrap()
如果内容是None
的话,unwrap()
一个可选值会导致panic。以下是在unwrap
时处理None的一些方法:
- unwrap_or_default(): 如果可选值是None,返回默认值。
- unwrap_or(): 提供一个替代值。
-unwrap_or_else(): 执行一个函数,如果可选值是None的话该函数返回某个值。
1 2 3 4 5 6 7 8 9 10 11 12
| fn main() { let x: Option<i32> = None; println!("{}", x.unwrap_or_default()); println!("{}", x.unwrap_or(99)); println!("{}", x.unwrap_or_else(|| "No value".to_string())); }
|
Tips #018
Rust 允许我们轻松定义匿名函数(也称为lambda
或 closure
或闭包)。闭包通常与 iterator
一起使用,或者用于定义回调函数。以下是语法示例:
1 2 3 4 5 6 7 8 9 10
| fn main() { let multiply = |a, b| a * b; let product = multiply(3, 4); println!("Product: {}", product); let add_two = |x| x + 2; let values = vec![1, 2, 3, 4, 5]; let new_values: Vec<_> = values.iter().map(add_two).collect(); println!("New values: {:?}", new_values); }
|
Tips #019
在闭包中使用 move
可以从周围作用域捕获变量。这意味着闭包将获取这些变量的所有权,而无需传递任何参数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| fn main() { let num = 5; let closure = || { println!("Num: {}", num); }; let closure_move = move || { println!("Num: {}", num); }; closure(); closure_move(); }
|
Tips #020
实现 From()
trait 提供了一种在复杂类型之间进行转换的机制。Rust 还会自动为对应的类型提供一个 Into()
实现。在下面的示例中,我们为 ColorFloat
实现了 From
,但是可以使用 Color8Bit
的 Into()
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| struct Color8Bit { r: u8, g: u8, b: u8, } struct ColorFloat { r: f32, g: f32, b: f32, } impl From<Color8Bit> for ColorFloat { fn from(color: Color8Bit) -> Self { ColorFloat { r: (color.r as f32) / 255.0, g: (color.g as f32) / 255.0, b: (color.b as f32) / 255.0, } } } fn main() { let color8 = Color8Bit { r: 230, g: 100, b: 50 }; let color_float: ColorFloat = color8.into(); println!("Color Float: {}, {}, {}", color_float.r, color_float.g, color_float.b); }
|
整理自 Daily Rust @rustoftheday