码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • Rust常用特型之From和Into特型


    在Rust标准库中,存在很多常用的工具类特型,它们能帮助我们写出更具有Rust风格的代码。

    std::convert::From和std::convert::Into特型是一种用于转换的特型,它们会消耗某个类型的值,返回另一个类型的值。相对于AsRef和AsMut特型用来从一个类型借出另一个类型的引用,From和Into特型会获取参数值的所有权并对它进行转换,最后返回生成的新类型的值(注意不是引用)。

    他们两个特型的定义相当对称:

    trait Into<T>: Sized {
    fn into(self) -> T;
    }
    trait From<T>: Sized {
      fn from(other: T) -> Self;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    标准库自动实现了所有类型到它自己的转换,每个类型都实现了From和Into。注意这里的类型必须是Sized类型,否则违反这两个特型的定义,注意,特型定义中,T是否Sized是显式定义,所以默认它就是Sized 类型。

    虽然这两个特型提供的函数实现了相同的功能,但是它们却应用于不同的场景。

    通常使用Into 类型让你的函数可接受的参数更加灵活,例如,你可以写如下 代码:

    use std::net::Ipv4Addr;
    fn ping<A>(address: A) -> std::io::Result<bool>
    where A: Into<Ipv4Addr>
    {
      let ipv4_address = address.into();
      ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    这里,ping函数不仅能接收IPV4Addr作为参数,它也能接受u32或者[u8;4]类型的值。因为这些类型都实现了Into。(从含义上来讲,把一个IPV4地址当成一个32位的值或者长度为4的u8数组也是有意义的)。

    ping函数仅需要知道address参数实现了Into就行了,这里并不需要限定你实际调用时传递进去参数的类型。这里只有一种可能工作的情况,所以类型推断自动为你填充了相应内容。

    正向我们先前在学习AsRef时提到的,Into特型可以让函数实现类似C++多态的效果。当我们使用上面的ping函数定义时,你可以做下面的任意调用:

    println!("{:?}", ping(Ipv4Addr::new(23, 21, 68, 141))); // pass an Ipv4Addr
    println!("{:?}", ping([66, 146, 219, 98]));   // pass a [u8; 4]
    println!("{:?}", ping(0xd076eb94_u32));  // pass a u32
    
    • 1
    • 2
    • 3

    然而,From特型,却扮演了一个不同的角色。from函数主要用来作为一个构造器,它从其它类型的值产生一个当前类型的实例。例如,IPV4Addr除了有两个函数from_array和from_u32外,它还实现了From<[u8;4]>和From,因此我们可以写出如下代码:

    let addr1 = Ipv4Addr::from([66, 146, 219, 98]);
    let addr2 = Ipv4Addr::from(0xd076eb94_u32);
    
    • 1
    • 2

    我们可以让类型推断来确定到底适配哪个实现。

    给定一个恰当的From实现,Rust标准库会自动为你实现相对应的Into特型。当你定义自己的类型时,如果你的构造器只有单一参数,你应该采用实现From方式来编写它。这时,你会免费得到相应的Into实现。

    这里举个简单的例子吧,如A实现了From,那么B就自动实现了Into。过程很简单,直接在Into特型的into函数里调用A::from(B)即可。

    因为from和to转换函数会获取传递参数的所有权,转换可以在新构造的值上重用初始值的资源。例如,假设有如下代码:

    let text = "Beautiful Soup".to_string();
    let bytes: Vec<u8> = text.into();
    
    • 1
    • 2

    String的Into>实现简单的获得了原字符串的堆缓冲区并且重用它,并没有改变它,而是直接作为向量元素的缓冲区返回。这个转换并不需要重新分配空间或者复制文本。这是move使得实现更高效的另一个场景。

    这些转换同时也提供了一个优雅的方法来松绑一个值,它从一个约束型类型转换成一个更灵活的类型, 同时并不削弱约束类型的约束的保证。例如,String类型保证它的内容总是有效的UTF-8字符,它的可变方法会被仔细限制为确保不会引入无效的UTF-8字符。但是这个示例演示了一个字符串可以转化为普通的字节,然后你就可以在它上面做你想做的事了,例如压缩它,或者和其它不是UTF-8字符相链接。由于into函数会获取值的所有权,因此text变量在转换之后就变成未初始化的,意味着我们可以自由的访问前者的字符串缓冲区而不必打破字符串的任何约定。

    这里的意思是你可以将一个约束更多的类型转换成一个更灵活的类型,然后你就可以做更多的处理。

    然而,一个便宜的转换并不是Into或者From的一部分。相对的,AsRef和AsMut 转换被期望为便宜的(因为他们只是借出引用),From和Into转换有可能分配空间,进行复制或者包含对值内容的其它处理。例如,String实现了From<&str>,它会将字符串切片复制到一个新分配的堆上的缓冲区。std::collections::BinaryHeap实现了From>,它会根据具体的算法来比较和重新排序元素。

    注意,有时候会在From和Into上应用?操作符。它一般用于错误转换,将各种不同的错误转换成一个更广泛的统一的错误。使用?操作符可以让你少写很多代码。这个经常和thiserror::error搭配在一起使用,此时使用一个枚举定义一个错误。枚举的每个变量代表不同的错误。例如 枚举MyError作为函数返回值,而函数类的语句返回OtherError时,在其后面使用?操作符会调用相应的into方法,转换成MyError

    例如,假定有一个需求,需要读取二进制数据,然后转成10进制数字,最后输出UTF-8文本。这就意味着使用std::str::from_utf8和在i32上使用FromStr实现,它们会返回不同的错误类型。假设我们使用第七章定义的GenericError和GenericError类型,?操作符可以为你做这种自动转换。

    type GenericError = Box<dyn std::error::Error + Send + Sync +'static>;
    type GenericResult<T> = Result<T, GenericError>;
    fn parse_i32_bytes(b: &[u8]) -> GenericResult<i32> {
    	Ok(std::str::from_utf8(b)?.parse::<i32>()?)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    像绝大多数error类型一样,Utf8Error和ParseIntError都实现了Error特型,标准库增加了一个空From实现,它可以将任意实现了Error特型的对象转化为一个Box,我们可以使用?自动来转换。

    impl<'a, E: Error + Send + Sync + 'a> From<E>
    for Box<dyn Error + Send + Sync + 'a> {
      fn from(err: E) -> Box<dyn Error + Send + Sync + 'a> {
        Box::new(err)
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    如果不使用?操作符,而是使用match匹配的话,你需要写很多代码。通过使用两次?操作符,你只需要一行代码。

    其实这里的?操作符的作用是错误传递,如果一个Result结果返回的是Ok(v),则?操作符直接得到v的值,如果返回的是Err(e),则会终止函数运行并把Err(e)作为函数结果返回。由于函数的返回类型是一个更广泛的类型Result,因此Err(e)会自动转化为Err(u)。

    这里书中的示例和常用的thiserror示例基本是相同的,只不过一个是返回了自定义的枚举类型(实现了Error特型),一个是返回Error特型对象(当然特型对象更广泛了)。

    虽然Result定义中的E并没有约定E:Error,但一般都用于E:Error。

    在From和Into特型被添加到Rust标准库之前,Rust代码充斥着临时热转换特型和构造方法,每个都定义了一个单独的类型。From和Into特型统一化了这种转换,只要你遵循它,你可以很容易的使用,你的用户对此也会很熟悉。语言本身和其它库也可以依赖这些特型来实现一个标准的,范例性的转换封装。

    From和Into是不可失败特型,他们的API需要这种转换一定是成功的。不幸的是,许多转换复杂的多,例如,转换一个很大的i64整数到i32会丢失一些信息,转换后的结果可能并不是我们想要的。例如:

    let huge = 2_000_000_000_000i64;
    let smaller = huge as i32;
    println!("{}", smaller); // -1454759936
    
    • 1
    • 2
    • 3

    有好几种方式来处理这种情况,根据代码中的上下文环境,一个wrapping转换可能更合适。另一方面,数字信息和处理系统经常会使用saturating转换,这里超过最大值的数会限制为最大值。

    这里的说的意思是由于From和Into不可能失败,有时直接进行转换并不合适,例如上面的例子,直接转换得到了负数。因此要根据实际使用情况选择合适的方法,而不是简单的使用From和Into直接进行转换。

    总结

    本章重点讲了如下几点:

    1. From和Into将一种类型转换成另一种类型,他们是对称的,实现其中一种,Rust会自动帮你实现另外一种。
    2. From和Into会消耗转换的值,同时有可能重新分配空间和进行复制,因此不是便宜的转换。但某些场景下,Move方式更高效。
    3. From和Into可以通过?操作符自动转换,一般这种场景用于错误传递。
    4. From和Into不可失败,并且也不是万能的(有些场景不适用直接转换)。
    5. From和Into可以将一种约束类型转换为更广泛的类型而不打破这种约束。
    6. Into特型可以让你的函数参数更灵活,而From特型主要用于构造器。
  • 相关阅读:
    NGINX配置SSL支持
    Nginx之upstream被动式重试机制解读z
    Java测试框架:分享常用的Java测试框架,如JUnit, TestNG等,包括单元测试,集成测试,性能测试等
    JS的优化技巧
    【编程题 】Rational Arithmetic(详细注释 易懂)
    MyBatis-Plus介绍与项目起步讲解
    教你如何用纯CSS代码实现垂直居中
    FISCO BCOS(二十二)——— 游离节点、观察者节点和共识节点
    数据的标准化处理——基于python
    【无标题】
  • 原文地址:https://blog.csdn.net/weixin_39430411/article/details/138000283
  • 最新文章
  • 攻防演习之三天拿下官网站群
    数据安全治理学习——前期安全规划和安全管理体系建设
    企业安全 | 企业内一次钓鱼演练准备过程
    内网渗透测试 | Kerberos协议及其部分攻击手法
    0day的产生 | 不懂代码的"代码审计"
    安装scrcpy-client模块av模块异常,环境问题解决方案
    leetcode hot100【LeetCode 279. 完全平方数】java实现
    OpenWrt下安装Mosquitto
    AnatoMask论文汇总
    【AI日记】24.11.01 LangChain、openai api和github copilot
  • 热门文章
  • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
    奉劝各位学弟学妹们,该打造你的技术影响力了!
    五年了,我在 CSDN 的两个一百万。
    Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
    面试官都震惊,你这网络基础可以啊!
    你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
    心情不好的时候,用 Python 画棵樱花树送给自己吧
    通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
    13 万字 C 语言从入门到精通保姆级教程2021 年版
    10行代码集2000张美女图,Python爬虫120例,再上征途
Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1
正则表达式工具 cron表达式工具 密码生成工具

京公网安备 11010502049817号