Rust的From和Into特征:可能是最常用的转换类特征

说到FromInto,以及从他们中衍生出的TryFromTryInto,想必大家都不会陌生。它们不像BorrowAsRefToOwned这些默默工作在泛型里的特征,是绝大多数Rust开发者每天都会使用到的东西。今天我们就来加深一下对这四个特征的了解吧~

From和Into

如果说AsRefAsMut的功能是做“引用到引用”的转换的话,那么FromInto做的就是“值到值”的转换了:

pub trait From<T>: Sized {
    // Required method
    fn from(value: T) -> Self;
}

pub trait Into<T>: Sized {
    // Required method
    fn into(self) -> T;
}

纵观两个特征的签名,它们都消耗掉一个值来产生另一个值;这就是FromInto的第一个小特点了:它们会立即把参数消耗掉。

对实现了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文档中对FromInto的使用提出了以下的几条原则;它们中的一部分在技术角度上并无强制性,但遵循这些原则可以满足一般的用户预期:

  • 转换应当是万无一失的:如果转换可能失败,那么应该使用TryFrom代替,而不是在FromInto的实现中埋下隐患,甚至产生panic。
  • 转换应当是无损的:从语义上来讲,转换过程中不应该丢失或丢弃信息。例如,对i32: From<u16>来说,使用u16: TryFrom<i32>可以将前一个过程的转换结果恢复原始值;但是对u16u32来说,从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

TryFromTryInto的功能和上文中介绍过的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将来会被!替代。


这篇文章不算很长,简单地梳理了FromIntoTryFromTryInto四个用于“值到值”的转换的特征。下篇文章将会介绍DisplayToString特征。

posted @ 2024-02-29 23:13  Cinea  阅读(70)  评论(0编辑  收藏  举报