在Rust标准库中,存在很多常用的工具类特型,它们能帮助我们写出更具有Rust风格的代码。
我们前面学习了了From
和Into
特型,今天我们来学们一下两个相似的特型TryFrom
和TryInto
。看名字就知道他们是试图转换的意思,那为什么有试图转换?在前面的学习中我们也介绍过,From
和Into
是转换不能失败的,但是有时转换需求是会失败的,例如字符串转数字,你怎么把英文字母转成数字呢?所以转换可能成功,可能失败,这时就需要使用TryFrom
和TryInto
特型了。
由于Rust并不是很明确怎样从i64转换成i32,因此它并没有实现为i32
实现From
(但反过来实现了),因为这种转换会丢失信息,其它会丢失信息的数字转换也是一样没有实现。作为替代,i32实现了TryFrom
。同From
和Into
特型的关系一样,TryFrom
和TryInto
也是对称的,你实现了TryFrom
,则目标类型的TryInto
也自动实现了。
他们的定义仅是比From
和Into
特型复杂一点:
pub trait TryFrom<T>: Sized {
type Error;
fn try_from(value: T) -> Result<Self, Self::Error>;
}
pub trait TryInto<T>: Sized {
type Error;
fn try_into(self) -> Result<T, Self::Error>;
}
因为有可能转换失败,所以try_into
函数返回一个Result
。我们在处理返回值时可以根据返回的是否错误来决定下一步怎么办,例如
use std::convert::TryInto;
// Saturate on overflow, rather than wrapping
let smaller: i32 = huge.try_into().unwrap_or(i32::MAX);
上面的转换如果超过了i32的最大值,则返回错误,因此unwrap_or
函数会使用一个i32的最大值作为smaller的值。
这里直接使用默认值时无法进一步灵活选择,下面的代码在出错时根据转换的是正还是负值从而返回不同的值,此时unwrap_or_else的参数为一闭包函数。
let smaller: i32 = huge.try_into().unwrap_or_else(|_|{
if huge >= 0 {
i32::MAX
} else {
i32::MIN
}
});
在你自己的类型上实现可失败的转换也很容易。根据程序需求和功能不同,转换失败时的错误类型Error
可以比较简单或者稍微复杂。上面的例子中,由于唯一可能发生的错误为溢出,标准库使用了一个空结构体来代表错误发生本身这个信息,不再提供其它额外信息。相对的,复杂类型的转换有可能需要返回更多的关于错误的信息,例如下面的代码。
impl TryInto<LinearShift> for Transform {
type Error = TransformError;
fn try_into(self) -> Result<LinearShift, Self::Error> {
if !self.normalized() {
return Err(TransformError::NotNormalized);
}
...
}
}
上面的例子中,返回的TransformError
包含多个枚举变量,因此它返回值有附带进一步信息,而不像之前的示例只有溢出这一种错误。
From
和Into
用来处理相关类型的简单转换,TryFrom
和TryInto
拓展了这种转换,增加了错误处理,因为复杂转换可能会失败。这四种特型可以一起使用来在一个crate
里关联多种类型。