Rust 中的类型转换

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 应运而生,它们分别用于低廉的引用到引用、可变引用到可变引用的转换。

Note

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() 的行为相同)。

需要注意的是,由于历史原因,上述行为目前并不适用于所有可解引用的类型。例如,foo.as_ref() 的行为与 Box::new(foo).as_ref() 并不相同。实际上许多智能指针提供的 as_ref 实现只是返回指向被指向值的引用(并未对该值执行廉价的引用到引用转换)。然而,AsRef::as_ref 不应仅仅为解引用而使用;如果只是为了解引用,应使用 Deref 强制转换:

let x = Box::new(5i32);
// 避免写成
// let y: &i32 = x.as_ref();
// 更好的写法是
let y: &i32 = &x;

对于那些实现了 Deref 的类型,可以利用 Deref 来实现 AsRef

impl<T> AsRef<T> for SomeType
where
    T: ?Sized,
    <SomeType as Deref>::Target: AsRef<T>,
{
    fn as_ref(&self) -> &T {
        self.deref().as_ref()
    }
}

如果函数接收一个 AsRef<T> 的泛型参数,那意味着所有可以转换为 &T 的引用类型都可作为参数传入。例如:

// `String` 和 `&str` 都实现了 `AsRef<str>`
fn is_hello<T: AsRef<str>>(s: T) {
   assert_eq!("hello", s.as_ref());
}

let s = "hello";
is_hello(s);

let s = "hello".to_string();
is_hello(s);

AsMut

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

需要注意的是,由于历史原因,上述行为目前并不适用于所有可可变解引用的类型。例如,foo.as_mut() 的行为与 Box::new(foo).as_mut() 并不相同。实际上许多智能指针提供的 as_mut 实现只是返回指向被指向值的可变引用(并未对该值执行廉价的可变引用到可变引用的转换)。然而,AsRef::as_ref 不应仅仅为可变解引用而使用;如果只是为了可变解引用,应使用 Deref 强制转换:

let mut x = Box::new(5i32);
// 避免写成
// let y: &mut i32 = x.as_mut();
// 更好的写法是
let y: &mut i32 = &mut x;

对于那些实现了 DerefMut 的类型,可以利用 DerefMut 来实现 AsMut

impl<T> AsMut<T> for SomeType
where
    <SomeType as Deref>::Target: AsMut<T>,
{
    fn as_mut(&mut self) -> &mut T {
        self.deref_mut().as_mut()
    }
}

如果函数接收一个 AsMut<T> 的泛型参数,那意味着所有可以转换为 &mut T 的可变引用类型都可作为参数传入。

Important

一个类型可以有多个 AsMut 的实现。例如 Vec<T> 同时实现了 AsMut<Vec<T>>AsMut<[T]>

as 关键字

as 最常见的用途是将原生类型转换为其他原生类型,但它还有其他用途,包括将指针转换为地址、将地址转换为指针,以及将指针转换为其他指针等。

语法格式是 e as U。其中 e 是表达式,U 是无约束的类型。

e 的类型 U e as U 执行的转换
整数或浮点类型 整数或浮点类型 数值转换
枚举 整数类型 枚举转换
boolchar 整数类型 原生类型到整数的转换
u8 char u8char 转换
*T *V(当兼容时) 指针到指针转换
*T(其中 T: Sized 整数类型 指针到地址转换
整数类型 *V(其中 V: Sized 地址到指针转换
&m₁ [T; n] *m₂ T1 数组到指针转换
*m₁ [T; n] *m₂ T1 数组到指针转换
函数项 函数指针 函数项到函数指针转换
函数项 *V(其中 V: Sized 函数项到指针转换
函数项 整数 函数项到地址转换
函数指针 *V(其中 V: Sized 函数指针到指针转换
函数指针 整数 函数指针到地址转换
闭包2 函数指针 闭包到函数指针转换

参考资料

  1. 仅适用于 m₁mutm₂const 时。允许将 mut 引用或指针强转为 const 指针。 2

  2. 仅适用于那些不捕获任何环境变量的闭包。