rust类型转换

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

一、显式转换

(一)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类型,97let 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() {// TryFromassert_eq!(EvenNumber::try_from(8), Ok(EvenNumber(8)));assert_eq!(EvenNumber::try_from(5), Err(()));// TryIntolet 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。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/143891.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

蓝桥杯每日一题2023.9.27

4408. 李白打酒加强版 - AcWing题库 题目描述 题目分析 对于这题我们发现有三个变量&#xff0c;店&#xff0c;花&#xff0c;酒的数量&#xff0c;对于这种范围我们使用DP来进行分析。 dp[i][j][k]我们表示有i个店&#xff0c;j朵花&#xff0c;k单位酒的集合&#xff0c…

B树和B+树的介绍和对比,以及MySQL为何选择B+树

在计算机科学中&#xff0c;B树和B树是常用的数据结构&#xff0c;用于在大规模数据集上进行高效的插入、删除和查找操作。它们在数据库管理系统、文件系统等许多实际应用中发挥着重要作用。本文将深入介绍B树和B树的结构特点、实际应用方面以及它们的优缺点&#xff0c;并最后…

Unity3d中Scene场景2D模式下放大后UI元素后不显示的问题

如题&#xff1a;UI在game视图显示没有问题&#xff0c; 在Play状态下&#xff0c;在Sence视图查看UI对象的时候进行放大操作&#xff0c;然后UI就不显示了或者显示不全&#xff0c;缩小就恢复正常。这让我在Play模式下预览UI状态很麻烦。相关问题描述较少。 初步判定为摄像机…

力扣:111. 二叉树的最小深度(Python3)

题目&#xff1a; 给定一个二叉树&#xff0c;找出其最小深度。 最小深度是从根节点到最近叶子节点的最短路径上的节点数量。 说明&#xff1a;叶子节点是指没有子节点的节点。 来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 链接&#xff1a;力扣&#xff08;LeetCod…

Spring Cloud Gateway实战WebFlux解析请求体及抛出指定错误代码和信息

概述 基于Spring Cloud开发微服务时&#xff0c;使用Spring Cloud原生自带的Gateway作为网关&#xff0c;所有请求都需要经过网关服务转发。 为了防止恶意请求刷取数据&#xff0c;对于业务请求需要进行拦截&#xff0c;故而可在网关服务增加拦截过滤器。基于此&#xff0c;有…

VM虚拟机连接NAT虚拟网络并上网的总结

关键字 VMware、NAT、VM虚拟机、ip route get、网关、私有云 设置 虚拟网络 VMware虚拟网络管理器中显示当前所有VMware的虚拟网络&#xff0c;根据显示&#xff0c;这里是"VMnet8"网络是NAT模式&#xff08;寄主机只能存在一个NAT虚拟网络&#xff0c;也就是说&a…

制作PE启动盘

文章目录 ⭐️写在前面的话⭐️1、下载微PE2、格式化U盘3、安装PE到U盘4、下载镜像 ⭐️写在前面的话⭐️ &#x1f4d2;博客主页&#xff1a; 程序员好冰 &#x1f389;欢迎 【点赞&#x1f44d; 关注&#x1f50e; 收藏⭐️ 留言&#x1f4dd;】 &#x1f4cc;本文由 程序员好…

通俗易懂了解大语言模型LLM发展历程

1.大语言模型研究路程 NLP的发展阶段大致可以分为以下几个阶段&#xff1a; 词向量词嵌入embedding句向量和全文向量理解上下文超大模型与模型统一 1.1词向量 将自然语言的词使用向量表示&#xff0c;一般构造词语字典&#xff0c;然后使用one-hot表示。   例如2个单词&…

【STM32】IAP升级01 bootloader实现以及APP配置(主要)

APP程序以及中断向量表的偏移设置 前言 通过之前的了解 之前的了解&#xff0c;我们知道实现IAP升级需要两个条件&#xff1a; 1.APP程序必须在 IAP 程序之后的某个偏移量为 x 的地址开始&#xff1b; 2.APP程序的中断向量表相应的移动&#xff0c;移动的偏移量为 x&#xff…

深入理解 pytest.main():Python 测试框架的核心功能解析

前言 笔者平常运行pytest用例时&#xff0c;通常使用命令行方式&#xff0c;像这样 pytest -v pxl/test_dir/test_demo.py::TestDemo::test_my_var&#xff0c;执行某一条case&#xff0c;但每次命令行敲也挺麻烦的。那如何在python代码中调用pytest呢&#xff1f;带着疑问一…

APP开发费用计算方法

计算开发移动应用&#xff08;APP&#xff09;的费用涉及多个因素&#xff0c;包括项目的规模、复杂性、所需功能、技术选择、开发团队的经验、地理位置和市场需求等。以下是一些考虑开发APP费用的关键因素以及一般的费用计算方法&#xff0c;希望对大家有所帮助。北京木奇移动…

第八天:gec6818arm开发板和Ubuntu中安装并且编译移植mysql驱动连接QT执行程序

一、Ubuntu18.04中安装并且编译移植mysql驱动程序连接qt执行程序 1 、安装Mysql sudo apt-get install mysql-serverapt-get isntall mysql-clientsudo apt-get install libmysqlclient-d2、查看是否安装成功&#xff0c;即查看MySQL版本 mysql --version 3、MySQL启动…

PHP8中伪变量“$this->”和操作符“::”的使用-PHP8知识详解

对象不仅可以调用自己的变量和方法&#xff0c;也可以调用类中的变量和方法。PHP8通过伪变量“$this->”和操作符“::”来实现这些功能。 1.伪变量“$this->” 在通过对象名->方法调用对象的方法时&#xff0c;如果不知道对象的名称&#xff0c;而又想调用类中的方法…

【新版】系统架构设计师 - 层次式架构设计理论与实践

个人总结&#xff0c;仅供参考&#xff0c;欢迎加好友一起讨论 文章目录 架构 - 层次式架构设计理论与实践考点摘要层次式体系结构概述表现层框架设计MVC模式MVP模式MVVM模式使用XML设计表现层表现层中UIP设计思想 中间层架构设计业务逻辑层工作流设计业务逻辑层设计 数据访问层…

三维模型3DTile格式轻量化压缩处理重难点分析

三维模型3DTile格式轻量化压缩处理重难点分析 在对三维模型3DTile格式进行轻量化压缩处理的过程中&#xff0c;存在一些重要而又困难的问题需要解决。以下是几个主要的重难点&#xff1a; 1、压缩率和模型质量之间的平衡&#xff1a;压缩技术的目标是尽可能地减少数据大小&…

【机器学习】期望最大算法(EM算法)解析:Expectation Maximization Algorithm

【机器学习】期望最大算法&#xff08;EM算法&#xff09;&#xff1a;Expectation Maximization Algorithm 文章目录 【机器学习】期望最大算法&#xff08;EM算法&#xff09;&#xff1a;Expectation Maximization Algorithm1. 介绍2. EM算法数学描述3. EM算法流程4. 两个问…

【AI视野·今日NLP 自然语言处理论文速览 第四十一期】Tue, 26 Sep 2023

AI视野今日CS.NLP 自然语言处理论文速览 Tue, 26 Sep 2023 Totally 75 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Computation and Language Papers Physics of Language Models: Part 3.1, Knowledge Storage and Extraction Authors Zeyuan Allen Zhu, Yuanz…

Databend 源码阅读:配置管理

作者&#xff1a;尚卓燃&#xff08;PsiACE&#xff09;澳门科技大学在读硕士&#xff0c;Databend 研发工程师实习生 Apache OpenDAL(Incubating) Committer https://github.com/PsiACE 对于 Databend 这样复杂的数据库服务端程序&#xff0c;往往需要支持大量的可配置选项&am…

k8s安装master节点遇到问题解决

1、安装k8s-1.19安装文档地址&#xff1a; https://kuboard.cn/install/history-k8s/install-k8s-1.19.x.html 2、按照文档中内容执行完master节点的操作报异常&#xff1a; 在执行&#xff1a; curl -sSL https://kuboard.cn/install-script/v1.19.x/init_master.sh | sh …

npm安装心得(依赖库Python及node-sass依赖环境)

在使用vue的开发环境过程中&#xff0c;总会遇到这样哪样的安装或者打包错误&#xff0c; vue运行或打包常见错误如下&#xff1a; 1. npm install时 node-sass npm ERR command failed &#xff08;可能是node.js的版本和node-sass的版本不符&#xff0c;就是卸掉原来的node.…