在 Rust 核心库的 core::convert 模块中定义了一系列用于实现类型转换的特征。它们包括 AsRef、AsMut、From 与 Into、TryFrom 与 TryInto。
From 与 Into最简单的类型转换就是 From 与 Into 实现的从值到值的转换。它们共同定义了消耗源类型所有权的无条件转换。在这里,
From 或 Into 的类型实例会在调用实现的方法后自动失效;事实上,这两个特征是互为镜像的关系。从特征的名字就可以看出,实现了 From 的类型就获得了从彼类型转换到此类型的能力,而实现 Into 便是从此类型转换到彼类型的能力。举个例子:
我们经常使用的 String 类型就实现了 Into<Vec<u8>>,表明任何 String 都能被丝滑地转换为 Into<Vec<u8>>。现在我们可以对任何一个 String 调用 Into 提供的方法 into,结果是一个 Vec<u8>,且原先的 String 所有权被消耗。
而 String 类型还实现了 From<&str>,这表明任何 &str 都能无痛转换为 String。有一个经常用的从 &str 转换为 String 的方法:String::from,它接收一个 &str 并返回对应的 String,实际上这个方法就是 From 特征提供的。
从上面两个例子不难看出,这一对特征所定义的类型转换是互为逆向的。
以下是两个特征的定义细节:
pub trait From<T>: Sized {
// Required method
fn from(value: T) -> Self;
}
pub trait Into<T>: Sized {
// Required method
fn into(self) -> T;
}
文档中,官方建议尽量为类型实现 From,而不是选择 Into。这是因为当你为类型 U 实现 From<T> 时,Rust 会很贴心地自动为类型 T 实现 Into<U> 特征。但反过来却行不通。所以优先考虑实现 From,实在实现不了 From 再考虑 Into。
为什么只有
Into<U>能被自动实现,而From<T>却不行?这关系到孤儿原则(只有在特征或类型至少有一个属于当前 crate 时,我们才能对类型实现该特征。不能为外部类型实现外部特征)。
让我们考虑一个情景:
ExternalType是外部类型,而MyType来自当前 crate。假设我们手动为类型MyType实现了Into<ExternalType>,现在 Rust 又自动提供了类型ExternalType的From<MyType>实现,但是你发现了什么?
ExternalType是外部类型,而实现的特征From来自核心库,也就是同为外部特征。两者都不属于当前 crate,违反了孤儿原则!
而在为泛型函数指定特征约束时,应优先使用 Into 而非 From,这样能够确保那些仅实现了 Into 的类型也能被正常使用。
TryFrom 与 TryInto正如特征名所包含的那样,TryFrom 与 TryInto 和 From 与 Into 极为相似,它们唯一的区别在于 TryFrom 与 TryInto 所进行的是可能失败的转换。由于转换可能不成功,某些情况下需要产生错误,所以这两种特征的方法都返回 Result:
pub trait TryFrom<T>: Sized {
type Error;
// Required method
fn try_from(value: T) -> Result<Self, Self::Error>;
}
pub trait TryInto<T>: Sized {
type Error;
// Required method
fn try_into(self) -> Result<T, Self::Error>;
}
举个例子,i32 实现了 TryFrom<i64> 特征,这意味着:
let big_number = 1_000_000_000_000i64;
// 由于 `big_number` 过大,超出 `i32` 可表示范围
// 转换会失败
let try_smaller_number = i32::try_from(big_number);
assert!(try_smaller_number.is_err());
// `as` 执行的是强制转换
// 所以从 `i64` 转换到 `i32` 会静悄悄地发生截断
let smaller_number = big_number as i32;
assert_eq!(smaller_number, -727379968);
AsRef 与 AsMut前面提到 From 与 Into 实现的从值到值的转换,并且会消耗掉源类型的所有权。如果我想把某类型的引用转换为另一类型的引用呢?
AsRef 和 AsMut 应运而生,它们分别用于低廉的引用到引用、可变引用到可变引用的转换。
AsRef和AsMut的出现并不代表着From与Into不能进行引用到引用(或可变引用到可变引用)的转换。它们被设计出来是因为直接对类型
&U实现From<&T>会产生代价高昂的转换,而AsRef和AsMut的转换成本则低廉得多。
特征名不带 Try,表明 AsRef 和 AsMut 都是不允许失败的转换。
两个特征的定义如下:
pub trait AsRef<T: PointeeSized>: PointeeSized {
// Required method
fn as_ref(&self) -> &T;
}
pub trait AsMut<T: PointeeSized>: PointeeSized {
// Required method
fn as_mut(&mut self) -> &mut T;
}
观察定义,你会发现 AsRef 和 AsMut 的用法与 Into 更像,而不是 From。
AsRef如果内部类型是引用或可变引用,AsRef 会自动解引用(例如当 foo 的类型为 &mut Foo 或 &&mut Foo 时,foo.as_ref() 的行为相同)。
需要注意的是,由于历史原因,上述行为目前并不适用于所有可解引用的类型。
AsMut