Rust的From和Into特征:可能是最常用的转换类特征
说到From和Into,以及从他们中衍生出的TryFrom和TryInto,想必大家都不会陌生。它们不像Borrow、AsRef、ToOwned这些默默工作在泛型里的特征,是绝大多数Rust开发者每天都会使用到的东西。今天我们就来加深一下对这四个特征的了解吧~
From和Into
如果说AsRef和AsMut的功能是做“引用到引用”的转换的话,那么From和Into做的就是“值到值”的转换了:
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的第一个小特点了:它们会立即把参数消耗掉。
对实现了From<T>的类型U,标准库为T提供了Into<U>的实现;也就是说,在为U实现了From<T>之后,就可以直接使用T::into()来构造U了:
use std::fmt;
struct BeautifulString(String);
impl From<String> for BeautifulString {
fn from(mut value: String) -> Self {
value.push_str("(✪ω✪)");
Self(value)
}
}
impl fmt::Display for BeautifulString {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
fn main() {
let string = String::from("I am beautiful!");
let beautiful: BeautifulString = string.into(); // 看这里
println!("{}", beautiful); // I am beautiful!(✪ω✪)
}
除此以外,From是反身的,也就是说对任何类型T,都有T: From<T>。
我们该实现From还是Into?
既然impl From<T> for U之后可以自动获得impl Into<U> for T,那么我们自然应该优先实现From而不是Into了;仅仅当转换的一方不是当前crate的成员时,才应当考虑实现Into。最直观的例子就是我们可以为T实现Into<String>,但肯定不能为String实现From<T>,这违反了Rust的孤儿原则。
使用From和Into的原则
Rust文档中对From和Into的使用提出了以下的几条原则;它们中的一部分在技术角度上并无强制性,但遵循这些原则可以满足一般的用户预期:
- 转换应当是万无一失的:如果转换可能失败,那么应该使用
TryFrom代替,而不是在From和Into的实现中埋下隐患,甚至产生panic。 - 转换应当是无损的:从语义上来讲,转换过程中不应该丢失或丢弃信息。例如,对
i32: From<u16>来说,使用u16: TryFrom<i32>可以将前一个过程的转换结果恢复原始值;但是对u16和u32来说,从u32转换为u16便不是无损的,因此也不该实现u16: From<u32>。 - 转换应当是保值的:将
i8转换为u8是无损的——被转换为255的-1_i8可以被毫不费力地转换回255,但是我们不能因此就允许u8: From<i8>的存在——毕竟-1和255是截然不同的两个数字,转换过程不保值。又比如说,String: From<u32>是不存在的,因为身为1的数字和身为"1"的文本差别过大;而String: From<char>便是可以接受的,因为'1'和"1"都是文本。 - 转换应当是显而易见的:转换应当是两种类型之间唯一合理的选择。例如,从
[u8;4]转换成u32的过程可以有多种选择:使用小字序、大字序和本地字序,所以应当分别为每种字节序实现不同的转换方法,而不是实现u32: From<[u8;4]>。
TryFrom和TryInto
TryFrom和TryInto的功能和上文中介绍过的From/Into相同,但是它们可能会受控地失败:
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>;
}
通用实现
实现U: TryFrom<T>会自动实现T: TryInto<U>;TryFrom也是反身的,任何类型T都自动实现了TryFrom<T>。
小插曲:
T: TryFrom<T>是永远不会失败的,它的返回类型是Result<Self, Infallible>。正如Infallible的名字所言,它用于表示永远不会发生的错误。Infallible将来会被!替代。
这篇文章不算很长,简单地梳理了From、Into、TryFrom和TryInto四个用于“值到值”的转换的特征。下篇文章将会介绍Display和ToString特征。

浙公网安备 33010602011771号