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.mzph.cn/news/90181.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据结构与算法之链表: 基于链表实现js中的原型链原理与算法实现 (Typescript版)

常见数据类型的原型链指向 obj -> Object.prototype -> nullfunc -> Function.prototype -> Object.prototype -> nullarr -> Array.prototype -> Object.prototype -> nullobj是Object实例func是Function实例&#xff0c;也是Object实例arr是Array实…

配置OSPFv3基本功能 华为笔记

1.1 实验介绍 1.1.1 关于本实验 OSPF协议是为IP协议提供路由功能的路由协议。OSPFv2&#xff08;OSPF版本2&#xff09;是支持IPv4的路由协议&#xff0c;为了让OSPF协议支持IPv6&#xff0c;技术人员开发了OSPFv3&#xff08;OSPF版本3&#xff09;。 无论是OSPFv2还是OSPFv…

设计模式2、抽象工厂模式 Abstract Factory

解释说明&#xff1a;提供一个创建一系列相关或相互依赖对象的接口&#xff0c;而无需指定他们具体的类。 简言之&#xff0c;一个工厂可以提供创建多种相关产品的接口&#xff0c;而无需像工厂方法一样&#xff0c;为每一个产品都提供一个具体工厂 抽象工厂&#xff08;Abstra…

学校安全用电管理系统解决方案

随着科技的发展和进步&#xff0c;电力已成为我们日常生活和学习的重要支柱。然而&#xff0c;电力的使用也带来了一定的安全风险。特别是对于学校这个复杂而又活跃的环境&#xff0c;安全用电管理系统的角色显得尤为重要。 一、学校用电管理系统的现状 目前&#xff0…

win10默认浏览器改不了怎么办,解决方法详解

win10默认浏览器改不了怎么办&#xff0c;解决方法详解_蓝天网络 在使用Windows 10操作系统时&#xff0c;你可能会遇到无法更改默认浏览器的情况。这可能是因为其他程序或设置正在干扰更改。如果你也遇到了这个问题&#xff0c;不要担心&#xff0c;本文将为你提供详细的解决…

CSS基础

目录 一.CSS介绍 三种CSS的写法 1.内部样式 2.内联样式 3.外部表示 二.CSS选择器 1.标签选择器 2.类选择器 ​编辑 3.ID选择器 ​编辑 4.后代选择器 ​编辑 5.子选择器 6.并集选择器 7.伪类选择器 三.CSS常用属性值 1.字体设置 2.文本属性 1.文字颜色 2.文…

kafka 集群搭建 常用命令

1、集群搭建&#xff1a; <1> 将kafka 压缩包解压到某一目录 tar -zxvf kafka_2.12-3.5.1.tgz <2> 修改节点配置文件 vim config/server.properties broker.id0 log.dirs/tmp/kafka-logs <3> 将安装好的kafka 分发到其他服务器 scp -r kafka_2.12-2.4…

华为云云耀云服务器L实例评测 | 实例使用教学之简单使用:通过命令行管理华为云云耀云服务器

华为云云耀云服务器L实例评测 &#xff5c; 实例使用教学之简单使用&#xff1a;通过命令行管理华为云云耀云服务器 介绍华为云云耀云服务器 华为云云耀云服务器 &#xff08;目前已经全新升级为 华为云云耀云服务器L实例&#xff09; 华为云云耀云服务器是什么华为云云耀云服务…

深入理解传输层协议:TCP与UDP的比较与应用

目录 前言什么是TCP/UDPTCP/UDP应用TCP和UDP的对比总结 前言 传输层是TCP/IP协议栈中的第四层&#xff0c;它为应用程序提供服务&#xff0c;定义了主机应用程序之间端到端的连通性。在本文章&#xff0c;我们将深入探讨传输层协议&#xff0c;特别是TCP和UDP协议的原理和区别…

ES写入数据报错:retrying failed action with response code: 429

报错&#xff1a; 使用logstash导入分片数量为9的index发生错误,[logstash.outputs.elasticsearch] retrying failed action with response code: 429 ({"type">"es_rejected_execution_exception", "reason">"rejected execution …

在 .NET 8 Release Candidate 1 中推出 .NET MAUI:质量

作者&#xff1a;David Ortinau 排版&#xff1a;Alan Wang 今天&#xff0c;我们很高兴地宣布 .NET MAUI 在 .NET 8 Release Candidate 1 中已经可用&#xff0c;该版本带有适用于生产应用程序的正式许可证&#xff0c;因此您可以放心地将此版本用于生产环境。我们在 .NET 8 中…

IDEA 2019 Springboot 3.1.3 运行异常

项目场景&#xff1a; 在IDEA 2019 中集成Springboot 3.1.3 框架&#xff0c;运行异常。 <?xml version"1.0" encoding"UTF-8"?><project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSch…

C++之传指针、引用、vector<shared_ptr<string>>应用总结(二百三十三)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

Spring面试题13:Spring中ApplicationContext实现有哪些?Bean工厂和Applicationcontext有什么区别

该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:Spring中ApplicationContext实现有哪些? 在Spring框架中,有以下几种ApplicationContext的实现: ClassPathXmlApplicationContext:从类路径下的…

计算机网络的相关知识点总结

1.谈一谈对OSI七层模型和TCP/IP四层模型的理解&#xff1f; 不管是OSI七层模型亦或是TCP/IP四层模型&#xff0c;它们的提出都有一个共同的目的&#xff1a;通过分层来将复杂问题细化&#xff0c;通过各个层级之间的相互配合来更好的解决计算机中出现的问题。 说到分层&#xf…

算法通过村第十关-并归|黄金笔记|手撕并归排序

文章目录 前言并归排序的原理总结 前言 提示&#xff1a;有时我会担心你们发现我其实很普通。爱并不需要你与众不同。--查理麦克西《男孩、鼹鼠、狐狸和马》 并归排序算是经典的分治思想中的问题&#xff0c;这个非常典型的题目。 并归排序的原理 并归排序&#xff0c;简单来说…

Shiro高级及SaaS-HRM的认证授权

Shiro在SpringBoot工程的应用 Apache Shiro是一个功能强大、灵活的&#xff0c;开源的安全框架。它可以干净利落地处理身份验证、授权、企业会话管理和加密。越来越多的企业使用Shiro作为项目的安全框架&#xff0c;保证项目的平稳运行。 在之前的讲解中只是单独的使用shiro&…

成为吃鸡战场的王者!分享顶级战术干货,助您提高战斗力!

各位吃鸡战场的玩家们&#xff0c;欢迎来到本视频&#xff01;在这里&#xff0c;我将为您呈现一些与众不同的吃鸡干货&#xff0c;帮助您提高战斗力、轻松吃鸡&#xff01; 首先&#xff0c;让我们谈一谈作图工具推荐。绝地求生作图工具是吃鸡玩家们的必备利器。我将给大家推荐…

rust学习-http-server端

Get请求 curl "http://127.0.0.1:8000/get/cat?task_id=123&demo=1"Post请求 curl http://localhost:8000/set/monkey \ -H "Content-Type:application/json" \ -H Authorization:bearer \ -X POST \ -d {"name":"xiaoming", &…

基于Matlab实现蚁群算法求解TSP问题(附上源码+数据)

蚁群算法&#xff08;Ant Colony Optimization, ACO&#xff09;是一种基于蚂蚁群体行为的启发式优化算法&#xff0c;它模拟了蚂蚁在寻找食物时的行为方式。在旅行商问题&#xff08;Traveling Salesman Problem, TSP&#xff09;中&#xff0c;蚁群算法可以用来求解最短路径问…