当前位置: 首页 > article >正文

【Rust标准库中的convert(AsRef,From,Into,TryFrom,TryInto)】

Rust标准库中的convert(AsRef,From,Into,TryFrom,TryInto)

  • 为什么需要convert库
  • AsRef(不可变引用:多用于内部字段获取值)
  • From/Into Trait | TryFrom/TryInto Trait
      • From Trait:
      • TryFrom Trait:
    • From代码示例
    • Into使用方法示例
  • 总结


为什么需要convert库

在程序设计时,开发者一般会选择将一系列类型数据打包在一起,并有所限制,一是为了代码的简洁美观和复用,二是为了给予字段访问限制(如我们只通过初始化函数赋予字段值,外部程序需要引用到内部字段但不该修改它)
提到限制,会有开发者想到使用pub 关键字,没错,pub关键字可以将内部字段暴露给外部函数,但是破坏了封装性,且没有限制可变性,有人说我们可以自己写一个函数,也Ok,但是需要注意可变性的限制 ,一般情况下,我们都不希望外部程序可以通过除特定的方法之外修改我们的字段值。
Rust为开发者提供了一揽子转换方法,不论是从基础转为派生,还是反向转换均有trait,使用者只需实现trait,便可使用其中的转换方法并不失去封装性。

AsRef(不可变引用:多用于内部字段获取值)

假设有这样一种需求,某论坛需要获取登陆者的电话号码,以便在后续论坛举办活动时给会员发去活动邀请,所以我们就需要挑选合适的方式在代码中储存信息以及分析合理性。

  1. 我们需要一个struct元组来描述用户的电话号码(这里涉及到新类型模式,不展开了,仅以简单示例):
//UserInfo.rs
#[derive(Debug)]
pub struct Phone(String);
//我们实现了简单的new函数,并包含简单的号码正确性验证。
impl Phone {
    pub fn new(phone_number: &str) -> Result<Self, &'static str> {
        if phone_number.len() == 11 {
            Ok(Self(phone_number.to_string()))
        } else {
            Err("Invalid phone number")
        }
    }
}
//并将其封装到包mod.rs
pub(crate) mod UserInfo;

2.我们在main.rs,写一个sendmsg方法用以表示发送短信通知:

mod user_info;
use std::error::Error;

use user_info::UserInfo::Phone;

fn sendmsg(phonenum: &str) -> Result<String, Box<dyn Error>> {
    // Simulate a call to the phone number
    // For simplicity, we just return the number as a string
    Ok(phonenum.to_string())
}
fn main() {
    let result = Phone::new("13324533333");
    match result {
        Ok(phone) => sendmsg(phone.0),
        Err(e) => todo!(),
    };
}
//哪里会有问题?

没错,在sendmsg(phone.0) 时会有如下报错,field 0 of Phone is private
不论是将Phone中的String设置为pub,还是使用可变引用都破坏了我们代码的封装性。

正确地方法:实现trait AsRef:

//在UserInfo.rs中添加如下实现
impl AsRef<str> for Phone {
    fn as_ref(&self) -> &str {
        &self.0
    }
}

//main.rs中使用入下调用
let result: Result<Phone, &str> = Phone::new("13324533333");
    let _ = match result {
        Ok(phone) => sendmsg(phone.as_ref()),
        Err(_e) => todo!(),
    };

如此,我们既不破坏封装性,又可以使用Rust convert的系列方法。

From/Into Trait | TryFrom/TryInto Trait

用以值到值的转换,简单来说,From提供细分内部类型向外部总类型转换,Into可以理解为为了适配孤儿规则而存在的,当前的版本1.82.0一般情况下不会有人主动实现Into,由于Rust的一揽子trait ,实现了From就等于实现了Into,非常方便。
不论是实际应用上,还是Rust中的举例来讲,From确实非常适合工程下的错误处理。
From有如下特点:

  1. 涉及到的转换不可以失败。
  2. 转换必须无损,如不丢失数据
  3. 转换必须保值,即不丢失精度
  4. 必须式是显而易见的转换,如 AuthError—> ServerError
//仅作代码示例
#[derive(Debug)]
enum ServerError {//总类型
    DatabaseError(String),//细分类型
    NetworkError(std::io::Error),
    TimeoutError(VarError),
    AuthError(Error),
}

ps: 如果不符合上述,则需要使用TryFrom,同样的,实现了TryFrom等于实现了TryInto。

From Trait:

pub trait From<T>: Sized {
    // Required method
    fn from(value: T) -> Self;
}
//其直接返回类型。

TryFrom Trait:

pub trait TryFrom<T>: Sized {
    type Error;
    // Required method
    fn try_from(value: T) -> Result<Self, Self::Error>;
}
//其返回Result,允许失败。

From代码示例

use std::{
    env::VarError,
    fmt::{Display, Error},
};
//仅作代码示例
#[derive(Debug)]
enum ServerError {
    DatabaseError(String),
    NetworkError(std::io::Error),
    TimeoutError(VarError),
    AuthError(Error),
}
impl Display for ServerError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ServerError::DatabaseError(err) => write!(f, "Database error: {}", err),
            ServerError::NetworkError(err) => write!(f, "Network error: {}", err),
            ServerError::TimeoutError(err) => write!(f, "Timeout error: {}", err),
            ServerError::AuthError(err) => write!(f, "AuthError error: {}", err),
        }
    }
}
impl From<String> for ServerError {
    fn from(err: String) -> Self {
        ServerError::DatabaseError(err)
    }
}

impl From<std::io::Error> for ServerError {
    fn from(err: std::io::Error) -> Self {
        ServerError::NetworkError(err)
    }
}
impl From<Error> for ServerError {
    fn from(err: Error) -> Self {
        ServerError::AuthError(err)
    }
}

impl From<VarError> for ServerError {
    fn from(err: VarError) -> Self {
        ServerError::TimeoutError(err)
    }
}

fn handle_server_error() -> Result<(), String> {
    Err(String::from("handle_server_error"))
}
fn handle_server_error1() -> Result<(), std::io::Error> {
    Ok(())
}
fn handle_server_error2() -> Result<(), VarError> {
    Ok(())
}
fn handle_server_error3() -> Result<(), Error> {
    Ok(())
}

fn func() -> Result<(), ServerError> {
    handle_server_error()?;
    handle_server_error1()?;
    handle_server_error2()?;
    handle_server_error3()?;
    Ok(())
}

fn main() {
    match func() {
        Ok(_) => println!("Success"),
        Err(e) => eprintln!("Error: {:?}", e),
    }
}

如此,开发者既可以细分错误类型,并定制化输出内容,又可以统一result类型,并使用【?】将error向上抛出
同时,Rust也提供了thiserror,anyhow等错误处理包,通过扩展宏标记后,会生成大部分的样板代码,同时通过trace等方式,将上下文串联起来更加方便的排查问题。

Into使用方法示例

此示例代码来源于Rust By Example

use std::convert::Into;

#[derive(Debug)]
struct Number {
    value: i32,
}

impl Into<Number> for i32 {
    fn into(self) -> Number {
        Number { value: self }
    }
}

fn main() {
    let int = 5;
    // Try removing the type annotation
    let num: Number = int.into();
    println!("My number is {:?}", num);
}

总结

Rust中的转换,不仅仅使用方便,更重要的是可以告知阅读者我们在做什么。


http://www.kler.cn/a/374652.html

相关文章:

  • PetaLinux 内核输出信息的获取方式
  • Flink调优----反压处理
  • SpringCloud 系列教程:微服务的未来(二)Mybatis-Plus的条件构造器、自定义SQL、Service接口基本用法
  • C语言初阶【13】——打印一个数的每一位(递归和非递归实现)
  • vue3标签中的ref属性如何使用$refs获取元素
  • 谷歌浏览器的网络连接问题解决方案
  • PyQt5信号与槽一
  • 【抽代复习笔记】34-群(二十八):不变子群的几道例题
  • .net core中间件Polly
  • 【WPF】如何获取屏幕比例
  • BFH的原理及用法
  • 【VS中Git同步提交 报错:访问.vs/FileContentIndex/xxx.vsidx权限不允许】
  • DAO模式及单例模式
  • 查询引擎的演变之旅 | OceanBase原理解读
  • 2024 Rust现代实用教程 Borrowing借用 Lifetime生命周期
  • python将mongodb中的数据写入到postgresql中
  • 华为实时视频使用FLV播放RTSP流
  • ssm药店管理系统-计算机毕业设计源码81276
  • 【数据结构与算法】第7课—数据结构之队列
  • 超子物联网HAL库笔记:准备篇
  • Hive的数据存储格式
  • 设计模式 策略模式 场景Vue (技术提升)
  • WebMvcConfigurer
  • React 中useState 原理
  • JIME智创:抖音创作者的AI绘画与视频生成创作神器
  • 无人机之卫星通信技术篇