rust类型转换

2023-11-15

类型转换
类型转换分为隐式类型转换和显式类型转换。
隐式类型转换是由编译器完成的,开发者并未参与,所有又称强制类型转换。
显式类型转换是由开发者指定的,就是一般意义上的类型转换。

一、显式转换

(一)as
1.原生类型之间的转换
Rust不提供原生类型之间的隐式类型转换,但可以使用as关键字显式类型转换。

整数强转是最常用的强转。
整数强转就是二进制的重新解释。
从长的类型转到短的类型,要截取低位二进制。

语法格式

value as type

实例

let decimal = 65.4321_f32;
let integer: u8 = decimal;     // 错误。不提供隐式转换
let integer = decimal as u8;     // 正确。可以显式转换
let character = integer as char;     //正确
println!("Casting: {} -> {} -> {}", decimal, integer, character);

println!("1000 as a u16 is: {}", 1000 as u16); 
println!(" -1 as a u8 is : {}", (-1i8) as u8);
println!(" 128 as a i16 is: {}", 128 as i16);
println!(" 128 as a i8 is : {}", 128 as i8);
println!("1000 as a u8 is : {}", 1000 as u8);     // 1000 as u8 -> 232
println!("1000 as a i8 is : {}", 1000 as i8);     // 232的二进制补码是 -24
println!(" 232 as a i8 is : {}", 232 as i8);     

let c = 'a' as u8; // 将char类型转换为u8类型,97

let mut values: [i32; 2] = [1, 2];
let p1: *mut i32 = values.as_mut_ptr();
let first_address = p1 as usize; // 将p1内存地址转换为一个整数
let second_address = first_address + 4; // 4 == std::mem::size_of::<i32>(),i32类型占用4个字节,因此将内存地址 + 4
let p2 = second_address as *mut i32; // 访问该地址指向的下一个整数p2
unsafe { *p2 += 1; }
assert_eq!(values[1], 3);

2.完全限定语法
类型A可以实现多个Trait,有些Trait有同名的函数,如果使用A去调用的话,编译器无法判断应该调用哪个函数,所以必须将A强转为特定的Trait,以告知编译器如何做出判断。这种将类型A强转成trait的用法就是完全限定语法。

完全限定语法的语法格式

<Type as Trait>::function(receiver_if_method, next_arg, ...);

实例
Pilot和Wizard都拥有方法fly。类型Human实现了这两个trait。

trait Pilot {
     fn fly(&self);
}
trait Wizard {
     fn fly(&self);
}
struct Human;
impl Pilot for Human {
     fn fly(&self) {
          println!("This is your captain speaking.");
     }
}
impl Wizard for Human {
     fn fly(&self) {
         println!("Up!");
     }
}
impl Human {
     fn fly(&self) {
         println!("*waving arms furiously*");
     }
}

当调用Human实例的fly时,编译器默认调用直接实现在类型上的方法

fn main() {
     let person = Human;
     person.fly();
}
运行结果
*waving arms furiously*

为了调用Pilot 或Wizard的fly方法,在方法名前指定trait名向Rust澄清了我们希望调用哪个fly

fn main() {
     let person = Human;
     Pilot::fly(&person);
     Wizard::fly(&person);
     person.fly();
}
运行结果
This is your captain speaking.
Up!
*waving arms furiously*

person.fly()也可以写成Human::fly(&person)。

然而,成员函数没有self参数。当两个类型实现了同一trait,Rust就不能判断是哪一个类型

trait Animal {
     fn baby_name() -> String;
}
struct Dog;
impl Dog {
     fn baby_name() -> String {
         String::from("Spot")
     }
}
impl Animal for Dog {
     fn baby_name() -> String {
         String::from("puppy")
     }
}
fn main() {
     println!("A baby dog is called a {}", Dog::baby_name());
}
运行结果
A baby dog is called a Spot

Dog有一个成员函数baby_name。Dog还实现了Animal trait。Animal也有一个成员函数baby_name
在main调用了Dog::baby_name函数,它直接调用Dog之上的成员函数。
这并不是我们需要的。我们希望调用的是Animal trait的baby_name函数,这样能够打印出A baby dog is called a puppy。
为了消歧义需要使用 完全限定语法

fn main() {
     println!("A baby dog is called a {}", <Dog as Animal>::baby_name());
}
运行结果
A baby dog is called a puppy

3.类型和子类型相互转换
as转换可以用于类型和子类型之间的转换。比如&’static str和&'a str。'static生命期是整个进程存活期间有效的,而’a的生命期较短,我们称&'static是&'a的子类型,使用’static:'a来表示。as可以将父子类型自由转换,如&'static str as &'a str,这种做法的意义是为了满足某些函数对生命期的要求。

(二)From和Into特性
这是两个Trait,定义在标准库的convert模块中,其实他们做的是同一件事情
如果类型A实现了From<B>,则B类型实例调用into方法就可以转换为类型A。
例如,我们常见的字符串String类型实现了From(&str),那么&str就可以into()为String。
所以我们只需要实现From即可

1.From
From trait允许一种类型定义 “怎么根据另一种类型生成自己”,因此它提供了一种类型转换的简单机制。
比如,可以很容易地把 str 转换成 String:

let my_str = "hello";
let my_string = String::from(my_str);

也可以为自定义类型实现From

use std::convert::From;
#[derive(Debug)]
struct Number {
     value: i32,
}
impl From<i32> for Number {
     fn from(item: i32) -> Self {
         Number { value: item }
     }
}
fn main() {
     let num = Number::from(30);
     println!("My number is {:?}", num);
}

2.Into
Into trait就是把 From trait倒过来而已。如果你为你的类型实现了 From,那么同时你也就免费获得了 Into。

例子

use std::convert::From;
#[derive(Debug)]
struct Number {
     value: i32,
}
impl From<i32> for Number {
     fn from(item: i32) -> Self {
         Number { value: item }
     }
}
fn main() {
     let int = 5;
     let num: Number = int.into();
     println!("My number is {:?}", num);
}

(三)TryFrom 和 TryInto特性
类似于 From和Into,TryFrom 和 TryInto 是类型转换的通用trait。不同于From/Into的是,TryFrom和TryInto trait用于易出错的转换,也正因如此,其返回值是 Result 型。
例子

use std::convert::TryFrom;
use std::convert::TryInto;
#[derive(Debug, PartialEq)]
struct EvenNumber(i32);
impl TryFrom<i32> for EvenNumber {
     type Error = ();
     fn try_from(value: i32) -> Result<Self, Self::Error> {
         if value % 2 == 0 {
             Ok(EvenNumber(value))
         } else {
             Err(())
         }
     }
}
fn main() {
     // TryFrom
     assert_eq!(EvenNumber::try_from(8), Ok(EvenNumber(8)));
     assert_eq!(EvenNumber::try_from(5), Err(()));
     // TryInto
     let result: Result<EvenNumber, ()> = 8i32.try_into();
     assert_eq!(result, Ok(EvenNumber(8)));
     let result: Result<EvenNumber, ()> = 5i32.try_into();
     assert_eq!(result, Err(()));
}

(四)ToString 和 FromStr特性
1.ToString
要把其他类型转换成 String,只需要实现那个类型的 ToString trait。然而不要直接这么做,您应该实现fmt::Display trait,它会自动提供 ToString,并且还可以用来打印

例子

use std::fmt;
struct Circle {
     radius: i32
}
impl fmt::Display for Circle {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         write!(f, "Circle of radius {}", self.radius)
     }
}
fn main() {
     let circle = Circle { radius: 6 };
     println!("{}", circle.to_string());
}

当然也可以直接实现ToString
例子

use std::string::ToString;
struct Circle {
     radius: i32
}
impl ToString for Circle {
     fn to_string(&self) -> String {
         format!("Circle of radius {:?}", self.radius)
     }
}
fn main() {
     let circle = Circle { radius: 6 };
     println!("{}", circle.to_string());
}

2.FromStr
把字符串转成其他类型。只要目标类型实现了 FromStr trait,就可以用 parse 把字符串转换成目标类型。
标准库中已经给无数类型实现了 FromStr。如果要转换到自定义类型,要手动实现 FromStr。

turbofish语法
语法格式

::<>

当把字符串转换成整数类型时,rust编译器无法确定要转换成哪种类型的整数,是u32还是i32。此时需要使用turbofish语法

实例

fn main() {
     let parsed: i32 = "5".parse().unwrap();     //显式指定类型
     let turbo_parsed = "10".parse::<i32>().unwrap();     //turbofish语法
     let sum = parsed + turbo_parsed;
     println!{"Sum: {:?}", sum};
}

(五)AsRef和AsMut特性
标准库中还有AsRef和AsMut两个trait,可以将值分别转换为不可变引用和可变引用。
比如,我们想设计一个允许传入&String和&str的函数

fn main(){
     let str1 = "123".to_string();
     let str2 = "456";
     test(str1);
     test(str2);
}
fn test(s: impl AsRef<str>){
     println!("{}", s.as_ref());
}

如果我们自己定义一个类型,最好能实现AsRef这个Trait,它会给我们带来很多意想不到的实惠。

二、隐式转换

(一)自动强转点
发生自动强转的时机。
自动强转点有:
1.let语句中显式给出了类型。
例如,下面例子中 &mut 42自动强转成 &i8类型:

let _: &i8 = &mut 42;

2.静态项和常量项声明(类似于let语句)。
3.函数调用的参数
被强制的值是实参,它的类型被自动强转为形参的类型。
例如,下面例子中 &mut 42自动强转成 &i8类型:

fn bar(_: &i8) { }
fn main() {
     bar(&mut 42);
}

对于方法调用,接受者(self参数)只能使用非固定尺寸类型自动强转
4.实例化结构体、联合体或枚举变体的字段。
例如,下面例子中 &mut 42自动强转成 &i8类型:

struct Foo<'a> { x: &'a i8 }
fn main() {
     Foo { x: &mut 42 };
}

5.函数返回值
例如,下面例子中x将自动强转成 &dyn Display类型:

use std::fmt::Display;
fn foo(x: &u32) -> &dyn Display {
     x
}

如果在自动强转点中的表达式是传播型表达式,那么该表达式中的对应子表达式也是自动强转点。传播从这些新的自动强转点开始递归。传播表达式及其相关子表达式有:
1.数组字面量,其数组的类型为 [U; n]。数组字面量中的每个子表达式都是自动强转到类型U的自动强转点。
2.重复句法声明的数组字面量,其数组的类型为 [U; n]。重复子表达式是用于自动强转到类型U的自动强转点。
3.元组,其中如果元组是自动强转到类型 (U_0, U_1, …, U_n) 的强转点,则每个子表达式都是相应类型的自动强转点,比如第0个子表达式是到类型U_0的 自动强转点。
4.圆括号括起来的子表达式((e)):如果整个括号表达式的类型为U,则子表达式e是自动强转到类型U的自动强转点。
5.块:如果块的类型是U,那么块中的最后一个表达式(如果它不是以分号结尾的)就是一个自动强转到类型U的自动强转点。这里的块包括作为控制流语句的一部分的条件分支代码块,比如if/else,当然前提是这些块的返回需要有一个已知的类型。

(二)自动强转类型
1.T到U如果T是U的一个子类型 (反射性场景(reflexive case))
2.T_1到T_3当T_1可自动强转到T_2同时T_2又能自动强转到T_3 (传递性场景(transitive case))
注意这个还没有得到完全支持。
3.&mut T到 &T
4.*mut T到 *const T
5.&T到 *const T
6.&mut T到 *mut T
7.&T或 &mut T到 &U如果T实现了Deref Target = U。例如:

use std::ops::Deref;
struct CharContainer {
     value: char,
}
impl Deref for CharContainer {
     type Target = char;
     fn deref<'a>(&'a self) -> &'a char {
         &self.value
     }
}
fn foo(arg: &char) {}
fn main() {
     let x = &mut CharContainer { value: 'y' };
     foo(x); //&mut CharContainer自动强转成 &char.
}

8.&mut T到 &mut U如果T实现了DerefMut Target = U
9.TyCtor(T) 到TyCtor(U),其中TyCtor(T) 是下列之一。TyCtor为类型构造器type constructor的简写。
&T
&mut T
*const T
*mut T
Box
并且U能够通过非固定尺寸类型自动强转得到。
10.非捕获闭包到函数指针
11.! 到任意T

(三)非固定尺寸类型自动强转
将固定尺寸类型转换为非固定尺寸类型
如果T可以自动强转成U,那么就会为T提供一个Unsize<U> 的内置实现:
1.[T; n] 到 [T]
2.T到dyn U, 当T实现U + Sized, 并且U是对象安全的时。
3.Foo<…, T, …> 到Foo<…, U, …>, 当:
Foo是一个结构体。
T实现了Unsize<U>
Foo的最后一个字段是和T相关的类型。
如果这最后一个字段是类型Bar<T>,那么Bar<T> 实现了Unsized<Bar<U>>
T不是任何其他字段的类型的一部分。
此外,当T实现了Unsize<U> CoerceUnsized<Foo<U>> 时,类型Foo 可以实现CoerceUnsized<Foo<U>>。这就允许Foo<T> 提供一个到Foo<U> 的非固定尺寸类型自动强转。
注:虽然非固定尺寸类型自动强转的定义及其实现已经稳定下来,但 Unsize 和 CoerceUnsized 这两个trait本身还没稳定下来,因此还不能直接用于稳定版的Rust。

(四)最小上界自动强转
在某些上下文中,编译器必须将多个类型强转到最公共的类型。这被称为“最小上界(Least Upper Bound,简称LUB)”自动强转。LUB自动强转只在以下情况中使用:
1.为一系列的if分支查找共同的类型。
2.为一系列的match分支查找共同的类型。
3.为数组元素查找共同的类型。
4.为带有多个返回语句的闭包查找共同的返回类型。
5.检查带有多个返回语句的函数的返回类型。

将一组类型T0…Tn自动强转到目标类型T_t,注意开始时T_t是未知的。LUB自动强转的计算过程是不断迭代的。首先把T_t定为T0。对于每一种新类型Ti,执行如下步骤:
1.如果Ti可以自动强转为当前目标类型T_t,则不做任何更改。
2.否则,检查T_t是否可以被自动强转为Ti;如果是这样,T_t就改为Ti。(此检查还取决于到目前为止所考虑的所有源表达式是否带有隐式自动强转。)
3.如果不是,尝试计算一个T_t和Ti的共同的超类型(supertype),此超类型将成为新的目标类型。
示例:

// if分支的情况
let bar = if true {
     a
} else if false {
     b
} else {
     c
};
// 匹配臂的情况
let baw = match 42 {
     0 => a,
     1 => b,
     _ => c,     
};
// 数组元素的情况
let bax = [a, b, c];
// 多个返回项语句的闭包的情况
let clo = || {
     if true {
         a
     } else if false {
          b
     } else {
         c
     }
};
let baz = clo();
// 检查带有多个返回语句的函数的情况
fn foo() -> i32 {
     let (a, b, c) = (0, 1, 2);
     match 42 {
         0 => a,
          1 => b,
         _ => c,
     }
}

在这些例子中,ba* 的类型可以通过LUB自动强转找到。编译器检查LUB自动强转在处理函数foo时,是否把a,b,c的结果转为了i32。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

rust类型转换 的相关文章

随机推荐

  • 牛客题目——最长无重复子数组、分糖果问题、旋转数组

    文章目录 题目1 最长无重复子数组 解题思路 代码实现 题目2 分糖果问题 解题思路 代码实现 题目3 旋转数组 解题思路 代码实现 题目1 最长无重复子数组 给定一个长度为n的数组arr 返回arr的最长无重复元素子数组的长度 无重复指的
  • volatility内存取证分析与讲解(持续更新)

    volatility内存取证分析与讲解 0x01 volatility的安装 0x02 基本使用 0x03 取证实战 持续更新 0x04 总结 0x01 volatility的安装 本人暂时只使用windows下的volatility进行取
  • AttributeError: Model object has no attribute predict_classes 的解决方案

    第一次用的网络是在model Sequential 下添加模块的的方法 也就是所谓的顺序模型 Sequential class可以使用model predict classes 的方法来实现预测 代码如下 model Sequential
  • android 七巧板布局,iOS界面视图布局框架 – TangramKit

    TangramKit logo TangramKit是一套在Swift3 0语言上开发的iOS界面视图布局框架 它的名字来源于中国古代的玩具七巧板 寓意着可以用简单的功能来构造出各种千变万化且非常复杂的UI界面 TangramKit的内核是
  • 静态分析之数据流分析与 SSA 入门 (二)

    什么是静态单赋值 SSA SSA 是 static single assignment 的缩写 也就是静态单赋值形式 顾名思义 就是每个变量只有唯一的赋值 以下图为例 左图是原始代码 里面有分支 y 变量在不同路径中有不同赋值 最后打印 y
  • 【pytorch】使用model.eval()和torch.no_grad()以及requires_grad = False之间的区别

    model eval 是将模型切换到评估模式 这意味着在模型中使用的一些操作 例如Dropout和BatchNorm 将不会在评估模式下运行 而是使用预定义的值 这对于在测试集上进行推理时很有用 with torch no grad 是一个
  • 【LaTeX入门】11 文本居中

    首先给大家分享一个巨牛巨牛的人工智能教程 是我无意中发现的 教程不仅零基础 通俗易懂 而且非常风趣幽默 还时不时有内涵段子 像看小说一样 哈哈 我正在学习中 觉得太牛了 所以分享给大家 点这里可以跳转到教程 centerline 语法 ce
  • App违法违规收集使用个人信息自评估指南(史宾格隐私合规检查项)

    隐私政策文本 隐私政策的独立性 易读性 是否有隐私政策 在APP界面中能够找到隐私政策 包括通过弹窗 文本链接 常见问题等形式 隐私政策是否单独成文 隐私政策以单独成文的形式发布 而不是作为用户协议 用户说明等文件中的一部分存在 隐私政策是
  • 【Java】无数据源启动

    前言 在开发项目的过程中 经常碰到框架使用SpringBoot进行开发 但是却不需要连接数据库的方式 本篇文章详细记录了具体的解决方法与方式希望能对您有所帮助 一 启动类配置 代码如下 import org springframework
  • 有哪些老程序员都知道对新程序员很有用的经验

    回想起自己刚步入职场的时候 接到任务的心态就是尽快搞完 只要没做完就怕耽误了整个团队 还怕领导觉得自己能不行 怕被开除等等 但是每次完成之后 都有错误 编译通过了 逻辑又有问题 自己就是不断的修改当中 时间久了自己写的代码自己都不愿意看 因
  • CMMI 级别中和BUG率

    关于CMMI 级别中和BUG率相关的信息如下 千行代码缺陷率 bug率 CMM1级 11 95 CMM2级 5 52 CMM3级 2 39 CMM4级 0 92 CMM5级 0 32 基本属于成倍递减 国内通过CMMI 5 级评定的IT行业
  • 【华为OD统一考试B卷

    华为OD统一考试A卷 B卷 新题库说明 2023年5月份 华为官方已经将的 2022 0223Q 1 2 3 4 统一修改为OD统一考试 A卷 和OD统一考试 B卷 你收到的链接上面会标注A卷还是B卷 请注意 根据反馈 目前大部分收到的都是
  • C/C++编程题刷题:leetcode 62. 不同路径 和 63. 不同路径 II

    62 不同路径 一个机器人位于一个 m x n 网格的左上角 起始点在下图中标记为 Start 机器人每次只能向下或者向右移动一步 机器人试图达到网格的右下角 在下图中标记为 Finish 问总共有多少条不同的路径 例如 上图是一个7 x
  • Filter(过滤器)常见应用

    javaweb学习总结 四十六 Filter 过滤器 常见应用 一 统一全站字符编码 通过配置参数charset指明使用何种字符编码 以处理Html Form请求参数的中文问题 1 package me gacl web filter 2
  • 【深度学习】Faster R-CNN+win10+tensorflow1.12.0+python3.6+CUDA9.0+cudnn7.3配置

    Faster R CNN win10 tensorflow1 12 0 python3 6 CUDA9 0 cudnn7 3配置 一 源码下载 二 修改 lib 下部分文件 编译Cython模块 三 安装COCO API 四 下载数据 五
  • 【Leetcode】225. 用队列实现栈

    题目描述 请你仅使用两个队列实现一个后入先出 LIFO 的栈 并支持普通栈的全部四种操作 push top pop 和 empty 实现 MyStack 类 void push int x 将元素 x 压入栈顶 int pop 移除并返回栈
  • Selenium+Firefox的自动下载(去掉下载弹窗)

    Selenium Firefox的自动下载 去掉下载弹窗 一 去掉下载弹窗的优点 二 去掉下载弹窗的一般命令 三 重点 一 去掉下载弹窗的优点 检索键盘鼠标自动化控制模块的导入 可以无头化运行 不影响同时进行的其他的任务 二 去掉下载弹窗的
  • Apache Avro 文档概况

    文章主题来源于英文源文档 gt Apache Avro 1 11 0 文档 Apache Avro 1 11 0 Documentation 1 介绍 Apache Avro 是一个数据序列化 data serialization 框架 A
  • CentOS 7 下安装Chrome浏览器

    参考 http www cnblogs com hfyfpga p 6261819 html http blog csdn net johnnyhu90 article details 42127521 如果出现如下错误 curl 7 Fa
  • rust类型转换

    类型转换 类型转换分为隐式类型转换和显式类型转换 隐式类型转换是由编译器完成的 开发者并未参与 所有又称强制类型转换 显式类型转换是由开发者指定的 就是一般意义上的类型转换 一 显式转换 一 as 1 原生类型之间的转换 Rust不提供原生