在Rust标准库中,存在很多常用的工具类特型,它们能帮助我们写出更具有Rust风格的代码。
某些类型可以有一个有意义的默认值,例如默认的向量或者字符串是空的,默认的数字为0,默认的Option是None
等。
这样的类型可以实现std::default::Default
特型:
trait Default {
fn default() -> Self;
}
该特型很简单,就只有一个关联函数default
,用于返回自身类型的一个新的值。
我们来看字符串String对该特型的实现,非常简单:
impl Default for String {
fn default() -> String {
String::new()
}
}
所以字符串的默认值就是一个新的空字符串.所有Rust的集合类型,Vec,HashMap,BinaryHeap等,都实现了Default特型,它们的default函数返回一个空的集合。这在你需要先建立一个集合,以后再向里面插入值时非常有用,因为你建立集合时不需要指定其中元素的类型,在第一次插入值时决定。
例如 Iterator
特型的partition
函数 把迭代器产生的值分离成两个集合,它使用一个闭包函数来决定哪个值去哪个集合。
use std::collections::HashSet;
let squares = [4, 9, 16, 25, 36, 49, 64];
let (powers_of_two, impure): (HashSet<i32>, HashSet<i32>)
= squares.iter().partition(|&n| n & (n-1) == 0);
assert_eq!(powers_of_two.len(), 3);
assert_eq!(impure.len(), 4);
闭包函数使用位操作符来查找哪些数是2的幂,partition
函数使用它产生两个HashSet
。当然,partition
函数并不是只能产生HashSet
,你可以使用它产生任何类型的集合,只要该集合实现了Default
和Extend<T>
特型,前者它会产生一个新的空集合,后者是将产生的值添加到集合中去。String 也实现了Default
和Extend<char>
特型,因此你可以这样写:
let (upper, lower): (String, String)
= "Great Teacher Onizuka".chars().partition(|&c|c.is_uppercase());
assert_eq!(upper, "GTO");
assert_eq!(lower, "reat eacher nizuka");
Default特型另一个常见的应用场景为多字段结构体赋值。如果一个结构体有很多字段,但是其中绝大多数字段的值是固定的,很少变化的。因此我们构造结构体时只需要给出少数几个需要设定的字段就行了,其它的固定字段使用一个默认值就行。例如glimu::DrawParameters
结构体有24个字段,每个字段都用来控制OpenGL怎么渲染图形中的某些位。而glium draw
函数使用一个DrawParameters
结构体作为参数,由于它实现了Default
,你可以这样写:
let params = glium::DrawParameters {
line_width: Some(0.02),
point_size: Some(0.02),
.. Default::default()
};
target.draw(..., ¶ms).unwrap();
这里我们只需要写出该结构体的两个字段 line_width和point_size,其它的都使用默认值。Default::default()
会创建一个包含默认初始化值的DrawParameters
结构体(这里为什么创建的值的类型是DrawParameters?因为Rust会自动推断,当然你也可以写 DrawParameters::default(),这样会多拼几个字母)。..
语法是解构,和Javascript类似,就是余下的字段全部从这里获取,但是有一眯注意,它必须出现在结构体构造的最后一行(Javascript没有这个限制)。
如果一个T类型实现了Default
,Rust 标准库也为Rc<T>,Arc<T>Box<T>, Cell<T>, RefCell<T>, Cow<T>, Mutex<T>
和
RwLock<T>
自动实现了Default特型。例如,Rc<T>
的默认值就是一个指向T默认值的Rc
指针。如果元组的所有元素实现了Default
,那么该元组(tuple)也自动实现了Default特型。它的默认值就是一个容纳了每个元素默认值的元组。
Rust 并没有为结构体自动实现Default
特型。但是如果结构体所有的字段都实现了Default
,那么你可以简单的在结构体定义上面添加#derive(Default)]
来为该结构体自动实现Default特型。如果该结构体不符合这个条件,那么即使你手动添加了派生宏,也不会有效果。