标准库简介
除了前面介绍过的容器、迭代器之外,标准库还提供了一系列有用的类型、函数、 trait 等。本章挑选其中比较常见的一部分简单介绍。
类型转换
Rust 给我们提供了一个关键字as 用于基本类型的转换。但是除了基本类型之外,还有 更多的自定义类型,它们之间也经常需要做类型转换。为此, Rust 标准库给我们提供了一系 列 的trait 来辅助抽象。
AsRef/AsMut
AsRef 这个trait 代表的意思是,这个类型可以通过调用as_ref方法,得到另外一个 类型的共享引用。它的定义如下:
js
pub trait AsRef < T: ?Sized > {
fn as_ref( & self) - >&T;
}
同 理 ,ASMut 有 一 个 as_mut方法,可以得到另外一个类型的可读写引用:
js
pub trait ASMut < T: ?Sized > {
fn as_mut( & mut self) - >&mut T;
}
比如说,标准库里面的String类型,就针对好几个类型参数实现了AsRef trait:
js
impl AsRef < str >
for String impl AsRef < [u8] >
for String impl AsRef < 0sStr >
for String impl AsRef < Path >
for String
AsRef 这样的trait 很适合用在泛型代码中,为一系列类型做统一抽象。比如,我们可 以写一个泛型函数,它接受各种类型,只要可以被转换为&[u8] 即可:
js
fn iter_bytes < T: AsRef < [u8] >> (arg: T) {
for i in arg.as_ref() {
println ! ("{}", i);
}
}
fn main() {
let s: String = String: :from("this is a string");
let v: Vec < u8 >= vec ! [1, 2, 3];
let c: &str = "hello";
}
相当于函数重载。只不过基于泛型实现的重载,一定需要重载的参数类型满足某种共同的约束 iter_bytes(s);
js
{
iter_bytes(V);
iter_bytes(c);
}
Borrow/BorrowMut
Borrow 这个trait 设计得与AsRef 非常像。它是这样定义的:
js
pub trait Borrow < Borrowed: ?Sized > {
fn borrow( & self) - >&Borrowed;
}
可以说,除了名字之外,它和AsRef 长得一模一样。但它们的设计意图不同。比如, 针对String 类型,它只实现了一个Borrow:
js
impl Borrow<str>for String
这是因为这个trait 一般被用于实现某些重要的数据结构,比如HashMap:
js
impl HashMap {
pub fn get < Q: ?Sized > ( & self, k: &Q) - >Option < &V > where K: Borrow < Q > ,
Q: Hash + Eq {}
}
和 BTreeMap:
js
impl BTreeMap {
pub fn get < Q: ?Sized > ( & self, key: &Q) - >Option < &V > where K: Borrow < Q > ,
Q: Ord {}
}
所以,它要求borrow() 方法返回的类型,必须和原来的类型具备同样的hash值,以及排序。这是一个约定,如果实现Borrow trait的时候违反了这个约定,那么把这个类型放 到 HashMap 或者BTreeMap 里面的时候就可能出现问题。
From/Into
AsRef/Borrow做的类型转换都是从一种引用&T 到另一种引用&U的转换。而From/Into 做的则是从任意类型T 到 U 的类型转换:
js
pub trait From < T > {
fn from(T) - >Self;
}
pub trait Into < T > {
fn into(self) - >T;
}
显然,From 和 Into 是互逆的一组转换。如果T 实现了From++, 那 么U理应实现 Into 。 因此,标准库里面提供了这样一个实现:++
js
impl < T,
U > Into < U >
for T where U: From < T > {
fn into(self) - >U {
U: :from(self)
}
}
用自然语言描述,意思就是:如果存在U:From, 则实现T:Into++。++
正是因为标准库中已经有了这样一个默认实现,我们在需要给两个类型实现类型转换的 trait的时候,写一个From 就够了,Into 不需要自己手写。
比如,标准库里面已经给我们提供了这样的转换:
js
impl<a>From<&a str>for String
这意味着&str 类型可以转换为String 类型。我们有两种调用方式: 一种是通过 String::from(&str) 来使用,一种是通过&str::into() 来使用。它们的意思一样:
js
fn main() {
let s: &'static str ="hello";
let str1:String =s.into();
let str2:String =String::from(s);
}
另外,由于这几个trait很常用,因此Rust已经将它们加入到prelude中。在使用的时 候我们不需要写use std::convert::From;这样的语句了,包括AsRef、ASMut、Into、From、ToOwned 等。具体可以参见libstd/prelude/v1.rs源代码的内容。
标准库中还有一组对应的TryFrom/TryInto 两个trait, 它们是为了处理那种类型转 换过程中可能发生转换错误的情况。因此,它们的方法的返回类型是Result类型。
ToOwned
ToOwned trait 提供的是一种更"泛化"的Clone 的功能。Clone 一般是从&T 类型变 量创造一个新的T 类型变量,而ToOwned 一般是从一个&T 类型变量创造一个新的U类型 变 量 。
在标准库中,ToOwned 有一个默认实现,即调用clone 方法:
js
impl < T > ToOwned
for T where T: Clone {
type Owned = T;
fn to_owned( & self) - >T {
self.clone()
fn clone_into( & self, target: &mut T) {
target.clone_from(self);
}
}
}
但是,它还对一些特殊类型实现了这个trait 。比如:
js
impl < T: Clone > ToOwned
for [T] {
type Owned = Vec < T > ;
}
impl ToOwned
for str {
type Owned = String;
}
而且,很有用的类型Cow 也是基于ToOwned 实现的:
js
pub enum Cow < 'a,B>
where
B:'a + ToOwned + ?Sized,
{
Borrowed( & 'a B),
Owned(<B as ToOwned>::Owned),
}
// ToString trait提供了其他类型转换为String 类型的能力。
pub trait ToString{
fn to_string(&self)->String;
}
一般情况下,我们不需要自己为自定义类型实现ToString trait。因为标准库中已经提供 了 一 个 默 认 实 现 :
js
impl < T: fmt: :Display + ?Sized > ToString
for T {# [inline]
default fn to_string( & self) - >String {
use core: :fmt: :Write;
let mut buf = String: :new();
buf.write_fmt(format_args ! ("{}", self)).expect("a Display implementation return an error unexpectedly");
buf.shrink_to_fit();
buf
}
}
这意味着,任何一个实现了Display trait 的类型,都自动实现了ToString trait。而 Display trait 是可以自动derive 的,我们只需要为类型添加一个attribute 即可。
FromStr则提供了从字符串切片&str 向其他类型转换的能力。
js
pub trait FromStr {
type Err;
fn from_str(s: &str) - >Result < Self,
Self: :Err > ;
}
因为这个转换过程可能出错,所以from_str 方法的返回类型被设计为Result。正是因为有了这个trait, 所 以str类型才有了一个成员方法parse:
js
pub fn parse<F:FromStr>(&self)->Result<F,F::Err>{ ... }
所以我们可以写下面这样非常清晰直白的代码:
js
let four ="4".parse::<u32>();
assert_eq!(Ok(4),four);