Rust 1.82.0 标准库:From和Into

介绍

FromInto 两个 trait 均源自于 std::convert 模块,它们在类型转换当中扮演着重要角色。

  • FromInto 都会消耗原始类型的值(即获取其所有权),并将其转换为另一种类型的值,最终返回转换后的结果。

  • 应该始终优先实现 From 而不是 Into,因为实现 From 后会自动通过标准库中的通用实现提供对应 Into 的实现。

  • 为泛型函数指定 trait 约束时,优先使用 Into 而不是 From,这样对于只实现了 Into 而没有实现 From 的类型也可以作为参数来使用。

  • FromInto 在封装错误处理这块极为有用。

  • 实现了 FromInto 的类型在进行类型转换时不允许失败,如果你的类型在转换时允许出现失败,请实现 TryFromTryInto

定义

我们首先来看 FromInto 的定义:

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

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

它们等价于下面的形式:

trait From<T> where Self: Sized {
    fn from(value: T) -> Self;
}

trait Into<T> where Self: Sized {
    fn into(self) -> T;
}

Self: Sized 是一种类型约束,用于表示当前的类型 Self 必须是一个在编译时大小已知的类型,即实现了 Sized trait 的类型。

也就是说 FromInto 只能被那些在编译时大小已知的类型所实现。

trait Foo: Sized {
    fn func();
}

// ok
impl Foo for i32 {
    fn func() {}
}

// error
impl Foo for [i32] {
    fn func() {}
}

fn main() {
}

上述代码中,由于 [i32]DST 类型,编译期间大小未知,所以无法实现 Foo

示例

struct MyMutString {
    value: String,
}

struct MyString {
    value: String,
}

impl From<String> for MyMutString {
    fn from(mut value: String) -> Self {
        value.pop();
        Self {
            value,
        }
    }
}

impl From<String> for MyString {
    fn from(value: String) -> Self {
        Self {
            value,
        }
    }
}

fn main() {
    let string: String = "12345".to_string();

    let my_mut_string: MyMutString = MyMutString::from(string.clone());
    println!("{:?}", my_mut_string.value); // "1234"

    let my_mut_string: MyMutString = string.clone().into();
    println!("{:?}", my_mut_string.value); // "1234"

    let my_string: MyString = MyString::from(string.clone());
    println!("{:?}", my_string.value); // "12345"

    let my_string: MyString = string.clone().into();
    println!("{:?}", my_string.value); // "12345"
}

上述代码展示了如何实现从类型 String 到我们自定义类型 MyMutStringMyString 的转换。在此过程中,参考 MyMutStringFrom 的实现,我们甚至可以在转换时修改原始类型的值,然后再进行转换。

自动实现 Into

自定义类型在实现 From 后,即可直接调用 into 函数,这是因为标准库自动帮我们实现了 Into,我们来看看标准库是如何帮我们实现的:

impl<T, U> Into<U> for T where U: From<T>, {
    fn into(self) -> U {
        U::from(self)
    }
}

如果我们把 TU 分别换成 StringMyMutString 的话,代码就变成了:

impl<String, MyMutString> Into<MyMutString> for String where MyMutString: From<String>, {
    fn into(self) -> MyMutString {
        MyMutString::from(self)
    }
}

上述代码非常清晰明了,代码为 String 实现了 Into<MyMutString>,同时约束了 MyMutString 必须实现 From<String>,由于我们已经为 MyMutString 实现了 From<String>,所以满足此处的约束,编译器将自动为我们实现 Into<MyMutString>

into 函数的实现同样非常简单,直接调用的就是 MyMutString::from(self)

指定类型

当我们调用 into 函数进行变量绑定时,必须明确指定类型。

struct Foo {}
struct Goo {}

impl From<String> for Foo {
    fn from(_: String) -> Self {
        Foo {}
    }
}

impl From<String> for Goo {
    fn from(_: String) -> Self {
        Goo {}
    }
}

fn main() {
    let string: String = String::from("string");
    let who1 = string.clone().into();
    let who2: _ = string.clone().into();
}

上述代码没有明确指定类型,编译器就没法确定 String 是应该转换成 Foo 呢还是 Goo 呢还是其他什么类型呢?编译器犯糊涂了,那就选择果断报错吧:

在这里插入图片描述

按照编译器给出的指示,正确写法如下:

struct Foo {}
struct Goo {}

impl From<String> for Foo {
    fn from(_: String) -> Self {
        Foo {}
    }
}

impl From<String> for Goo {
    fn from(_: String) -> Self {
        Goo {}
    }
}

fn main() {
    let string: String = String::from("string");
    let foo: Foo = string.clone().into();
    let goo: _ = <String as Into<Goo>>::into(string.clone());
}

自反性

FromInto 都具有自反性,也就是为 T 实现 From<T>Into<T>。我们来看标准库的实现:

impl<T> From<T> for T {
    fn from(t: T) -> T {
        t
    }
}

Into<T> 会被标准库自动实现:

impl<T> Into<T> for T where T: From<T>, {
    fn into(self) -> T {
        T::from(self)
    }
}

由此我们可以写出如下代码:

struct Foo;

fn func1<T: From<T>>(_: T) {}
fn func2<T: Into<T>>(_: T) {}

fn main() {
    func1::<bool>(true);
    func1::<i32>(10_i32);
    func1::<Foo>(Foo);

    func2::<bool>(true);
    func2::<i32>(10_i32);
    func2::<Foo>(Foo);
}

以及:

struct Foo;

fn main() {
    let _: bool = bool::from(true);
    let _: bool = true.into();

    let _: i32 = i32::from(42);
    let _: i32 = 42.into();

    let _: Foo = Foo::from(Foo);
    let _: Foo = Foo.into();
}

封装错误处理

在进行错误处理时,为自己的自定义错误类型实现 From 通常很有用。

通过将底层错误类型转换为我们自己的自定义错误类型,我们可以在不丢失底层原因信息的情况下返回单一的错误类型。

? 操作符会自动使用 From::from 将底层错误类型转换为我们的自定义错误类型。

use std::fs;
use std::io;
use std::num;

enum CliError {
    IoError(io::Error),
    ParseError(num::ParseIntError),
}

impl From<io::Error> for CliError {
    fn from(error: io::Error) -> Self {
        CliError::IoError(error)
    }
}

impl From<num::ParseIntError> for CliError {
    fn from(error: num::ParseIntError) -> Self {
        CliError::ParseError(error)
    }
}

fn open_and_parse_file(file_name: &str) -> Result<i32, CliError> {
    let mut contents = fs::read_to_string(&file_name)?;
    let num: i32 = contents.trim().parse()?;
    Ok(num)
}
### 删除`config.toml`后执行`./configure`命令输出信息分析 `config.toml`文件是Rust构建系统的配置文件,包含了构建过程中的各种参数选项。删除该文件后执行`./configure`命令,会尝试重新生成默认的配置信息。 通常,`./configure`命令会进行一系列的检查,包括检查系统环境、依赖库等,以确定构建过程中所需的参数。输出信息可能包含以下几类: - **环境检查信息**:检查系统的编译器、工具链、依赖库等是否可用。例如,会检查`gcc`、`make`等工具是否安装,以及一些必要的开发库(如`openssl`等)是否存在。 - **配置参数设置信息**:根据检查结果,设置构建过程中的各种参数,如目标架构、构建类型(调试版或发布版)等。 - **错误警告信息**:如果在检查过程中发现问题,会输出相应的错误或警告信息。例如,如果缺少某个依赖库,会提示无法找到该库,可能导致某些功能无法正常构建。 示例输出可能如下: ```plaintext checking for gcc... /usr/bin/gcc checking whether the C compiler works... yes checking for C compiler default output file name... a.out checking for suffix of executables... checking whether we are cross compiling... no checking for suffix of object files... o checking whether we are using the GNU C compiler... yes checking whether /usr/bin/gcc accepts -g... yes checking for /usr/bin/gcc option to accept ISO C89... none needed checking for openssl... not found configure: WARNING: openssl library not found. Some features may not work correctly. ``` ### 运行`python /opt/rust-1.82.0/x.py --help`命令的相关解释 `x.py`是Rust项目的构建脚本,它封装了一系列的构建命令操作,提供了一种方便的方式来管理Rust的构建过程。运行`python /opt/rust-1.82.0/x.py --help`命令会输出该脚本的帮助信息,展示可用的命令选项。 帮助信息通常会包含以下内容: - **命令列表**:列出可以执行的各种构建命令,如`build`(构建Rust编译器标准库)、`test`(运行测试)、`doc`(生成文档)等。 - **选项说明**:解释每个命令可用的选项,例如可以指定构建的目标架构、构建类型(调试版或发布版)等。 示例帮助信息可能如下: ```plaintext Usage: x.py [OPTIONS] <COMMAND> Commands: build Build the Rust compiler and standard library test Run the test suite doc Generate documentation ... Options: --target <TARGET> Specify the target architecture --release Build in release mode --help Print this help message ``` ### 示例代码 以下是一个简单的示例,展示如何根据帮助信息使用`x.py`脚本进行构建: ```bash # 构建Rust编译器标准库,使用发布模式 python /opt/rust-1.82.0/x.py build --release ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值