Rust 中的 convert 特征

Rust 的类型转换机制

技术
技术Rust标准库编程语言特性

2026-03-26

在 Rust 核心库的 core::convert 模块中定义了一系列用于实现类型转换的特征。它们包括 AsRefAsMutFromIntoTryFromTryInto

FromInto

最简单的类型转换就是 FromInto 实现的从值到值的转换。它们共同定义了消耗源类型所有权无条件转换。在这里,

事实上,这两个特征是互为镜像的关系。从特征的名字就可以看出,实现了 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 又自动提供了类型 ExternalTypeFrom<MyType> 实现,但是你发现了什么?

ExternalType 是外部类型,而实现的特征 From 来自核心库,也就是同为外部特征。

两者都不属于当前 crate,违反了孤儿原则!

而在为泛型函数指定特征约束时,应优先使用 Into 而非 From,这样能够确保那些仅实现了 Into 的类型也能被正常使用。

TryFromTryInto

正如特征名所包含的那样,TryFromTryIntoFromInto 极为相似,它们唯一的区别在于 TryFromTryInto 所进行的是可能失败的转换。由于转换可能不成功,某些情况下需要产生错误,所以这两种特征的方法都返回 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);

AsRefAsMut

前面提到 FromInto 实现的从值到值的转换,并且会消耗掉源类型的所有权。如果我想把某类型的引用转换为另一类型的引用呢?

AsRefAsMut 应运而生,它们分别用于低廉的引用到引用、可变引用到可变引用的转换。

AsRefAsMut 的出现并不代表着 FromInto 不能进行引用到引用(或可变引用到可变引用)的转换。

它们被设计出来是因为直接对类型 &U 实现 From<&T> 会产生代价高昂的转换,而 AsRefAsMut 的转换成本则低廉得多。

特征名不带 Try,表明 AsRefAsMut 都是不允许失败的转换。

两个特征的定义如下:

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;
}

观察定义,你会发现 AsRefAsMut 的用法与 Into 更像,而不是 From

AsRef

如果内部类型是引用或可变引用,AsRef 会自动解引用(例如当 foo 的类型为 &mut Foo&&mut Foo 时,foo.as_ref() 的行为相同)。

需要注意的是,由于历史原因,上述行为目前并不适用于所有可解引用的类型。

AsMut