rust泛型

泛型,英文是generic。
泛型是一种参数化多态。就是把类型作为参数,使用时才指定具体类型。
这样一套代码可以应用于多种类型。比如Vec<T>,可以是整型向量Vec<i32>,也可以是浮点型向量Vec<f64>

Rust中的泛型属于静多态,它是一种编译期多态。
在编译期,会根据指定的具体类型,生成一套特化代码,这叫单态化。比如将一个泛型函数生成具体类型对应的函数。
单态化是编译器进行静态分发的一种策略。
单态化静态分发的好处就是性能好,没有运行时开销;缺点就是造成编译后生成的二进制文件膨胀。

一、定义泛型

语法格式
<T>
T就是泛型参数。

(一)在函数中使用泛型
使用了泛型的函数就叫泛型函数
1.语法格式
泛型函数的定义语法如下

fn function_name<T[:trait_name]>(param1:T, [other_params]) {// 函数实现代码
}

实例

fn max<T>(array: &[T]) -> T {let mut max_index = 0;let mut i = 1;while i < array.len() {if array[i] > array[max_index] {max_index = i;}i += 1;}array[max_index]
}

(二)在结构体中使用泛型
使用了泛型的结构体叫做泛型结构体
1.语法格式
泛型结构体的定义语法如下

struct struct_name<T> {field:T
}

例子

struct Point<T> {x: T,y: T
}
let p1 = Point {x: 1, y: 2};
let p2 = Point {x: 1.0, y: 2.0};

使用时并没有声明类型,这里使用的是自动推断机制,但不允许出现类型不匹配的情况如下:

let p = Point {x: 1, y: 2.0};

2.在结构体的实现块中使用泛型
语法格式

impl<T> Point<T> {
}

注意,impl关键字的后方必须有 <T>,因为Point<T>要以它为实参。

实例

struct Point<T> {x: T,y: T,
}
impl<T> Point<T> {fn x(&self) -> &T {&self.x}
}
fn main() {let p = Point { x: 1, y: 2 };println!("p.x = {}", p.x());
}
运行结果:
p.x = 1

也可以直接特化

impl Point<f64> {fn x(&self) -> f64 {self.x}
}

在成员方法中使用泛型
impl块本身的泛型不影响成员方法的泛型:

impl<T, U> Point<T, U> {fn mixup<V, W>(self, other: Point<V, W>) -> Point<T, W> {Point {x: self.x,y: other.y,}}
}

方法mixup将一个Point<T, U>点的x与Point<V, W>点的y融合成一个类型为Point<T, W>的新点。

(三)在枚举中使用泛型
使用了泛型的枚举叫泛型枚举
诸如Option和Result

enum Option<T> {Some(T),None
}enum Result<T, E> {Ok(T),Err(E)
}

(四)在trait中使用泛型

struct AS {}
trait AT<T> {fn foo(&self, para: T);
}impl AT<String> for AS{fn foo(&self, para: String){}
}

(五)在别名中使用泛型
比如

type IoResult<T>=Result<T, IoError>;

二、泛型参数约束

1.trait约束
限制参数为必须实现了某个特性

范例
传递的参数必须是实现了Display特性的类型。

use std::fmt::Display;
fn main(){print_pro(10 as u8);print_pro(20 as u16);print_pro("Hello TutorialsPoint");
}
fn print_pro<T:Display>(para:T){println!("Inside print_pro generic function:");println!("{}",para);
}
编译运行结果如下
Inside print_pro generic function:
10
Inside print_pro generic function:
20
Inside print_pro generic function:
Hello TutorialsPoint

2.多重约束
使用+
比如

fn notify<T: Summary + Display>(item: T){}

实例

struct StructA<T> {}
impl<T: TraitB + TraitC> StructA<T> {fn d(&self) {}
}

StructA<T>类型必须在T已经实现B和C特性的前提下才能实现此impl块。

3.使用where关键字简写约束
约束也可以使用where分句来表达,它放在 { 的前面。

例如:

fn some_function<T: Display + Clone, U: Clone + Debug>(t: T, u: U){
}

可以简化成:

fn some_function<T, U>(t: &T, u: &U) -> i32
where
T: Display + Clone,
U: Clone + Debug,{
}

下面的impl如果不用where从句,就无法直接表达。

use std::fmt::Debug;
trait PrintInOption {fn print_in_option(self);
}
// 这里需要一个 `where` 从句,否则就要表达成 `T: Debug`(这样意思就变了),
// 或者改用另一种间接的方法。
impl<T> PrintInOption for T
where
Option<T>: Debug {// 我们要将 `Option<T>: Debug` 作为约束,因为那是要打印的内容。// 否则我们会给出错误的约束。fn print_in_option(self) {println!("{:?}", Some(self));}
}
fn main() {let vec = vec![1, 2, 3];vec.print_in_option();
}

三、泛型参数默认值

1.当使用泛型时,可以为泛型参数指定一个默认的具体类型。
语法

 <PlaceholderType=ConcreteType>

这种情况的一个非常好的例子是运算符重载。运算符重载是指自定义运算符(比如 +)的行为。
Rust并不允许创建自定义运算符或重载任意运算符,不过std::ops中所列出的运算符和相应的trait可以通过实现运算符相关trait来重载。
例如,下例展示了如何在Point结构体上实现Add trait来重载 + 运算符,这样就可以将两个Point实例相加了

use std::ops::Add;
#[derive(Debug, PartialEq)]
struct Point {x: i32,y: i32,
}
impl Add for Point {type Output = Point;fn add(self, other: Point) -> Point {Point {x: self.x + other.x,y: self.y + other.y,}}
}
fn main() {assert_eq!(Point { x: 1, y: 0 } + Point { x: 2, y: 3 },Point { x: 3, y: 3 });
}

add方法将两个Point实例的x值和y值分别相加来创建一个新的Point。

Add定义如下

trait Add<RHS=Self> {type Output;fn add(self, rhs: RHS) -> Self::Output;
}

RHS=Self,Self就是泛型参数默认值,它表示实现Add的类型,在这里就是Point类型。如果实现Add时不指定RHS的具体类型,RHS的类型将是Self类型。

2.不使用默认类型的例子。
我们希望能够将毫米值与米值相加,并让Add的实现正确处理转换。可以为Millimeters实现Add并以Meters作为RHS

use std::ops::Add;
struct Millimeters(u32);
struct Meters(u32);
impl Add<Meters> for Millimeters {type Output = Millimeters;fn add(self, other: Meters) -> Millimeters {Millimeters(self.0 + (other.0 * 1000))}
}

为了使Millimeters和Meters能够相加,我们使用Add<Meters>而不是使用默认的Add。

四、泛型代码的性能

Rust通过在编译时单态化来保证效率。单态化是一个通过填充编译时使用的具体类型,将通用代码转换为特定代码的过程。
编译器寻找所有泛型代码被调用的位置并针对具体类型生成特化代码。
例子

let integer = Some(5);
let float = Some(5.0);

当Rust编译这些代码的时候,它会单态化。编译器会读取传递给Option<T>的值并发现有两种Option<T>:一个对应i32,另一个对应f64。为此,它会将泛型定义Option<T>展开为两个针对i32和f64的定义,接着将泛型定义替换为这两个具体的定义。
编译器生成的单态化代码看起来像这样:

enum Option_i32 {Some(i32),None,
}
enum Option_f64 {Some(f64),None,
}
fn main() {let integer = Option_i32::Some(5);let float = Option_f64::Some(5.0);
}

Option<T>被编译器替换为了具体的定义。因为Rust会将每种情况下的泛型代码编译为具体类型,使用泛型没有运行时开销。当代码运行时,它的执行效率就跟好像手写每个具体定义的重复代码一样。这个单态化过程正是Rust泛型在运行时极其高效的原因。

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

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

相关文章

Vue 07 Vue中的数据代理

通过数据代理&#xff0c;我可以方便的使用vm.属性&#xff0c;修改data中的属性 什么是数据代理 数据代理&#xff1a;通过一个对象代理对另一个对象中属性的操作&#xff08;读/写&#xff09; 我们修改obj2的x属性&#xff0c;其实修改的是obj的x属性 <!DOCTYPE html&…

基于springboot消防员招录系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…

设计模式:观察者模式(C++实现)

观察者模式&#xff08;Observer Pattern&#xff09;是一种设计模式&#xff0c;用于定义对象之间的一对多依赖关系&#xff0c;当一个对象&#xff08;称为主题或可观察者&#xff09;的状态发生变化时&#xff0c;它的所有依赖对象&#xff08;称为观察者&#xff09;都会收…

vue-cli创建项目、vue项目目录结(运行vue项目)、ES6导入导出语法、vue项目编写规范

vue-cli创建项目、vue项目目录结构、 ES6导入导出语法、vue项目编写规范 1 vue-cli创建项目 1.1 vue-cli 命令行创建项目 1.2 使用vue-cli-ui创建 2 vue项目目录结构 2.1 运行vue项目 2.2 vue项目的目录结构 3 es6导入导出语法 4 vue项目编写规范 4.1 修改项目 4.2 以后…

DDL、DML

文章目录 一、字段1.1 添加字段1.2 给列添加默认值1.3 修改列的属性1.4 更新1.5 删除1.6 同时修改多列 二、索引2.1 普通索引2.2 唯一键 三、创建表四、字段json不是json类型&#xff0c;但是也可以按照json存储内容补充 default 0 和 default 0没区别 一、字段 1.1 添加字段 …

【深度学习推荐系统 工程篇】三、浅析FastTransFormer看 GPU推理优化 思路

前言 在搜索/推荐场景中&#xff08;一般是CTR/CVR预估&#xff09;Serving的模型一般是稀疏参数占比比较大&#xff0c;工程落地方面会遇到两方面的困难&#xff1a; 稀疏参数的存储/IO网络结构的优化 对于稀疏参数的存储/IO&#xff0c;在上一篇【深度学习推荐系统 工程篇…

电子信息工程专业课复习知识点总结:(五)通信原理

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 第一章通信系统概述——通信系统的构成、各部分性质、性能指标1.通信系统的组成&#xff1f;2.通信系统的分类&#xff1f;3.调制、解调是什么&#xff1f;有什么用…

详解MySQL存储引擎

前言: 📕作者简介:热爱编程的小七,致力于C、Java、Python等多编程语言,热爱编程和长板的运动少年! 📘相关专栏Java基础语法,JavaEE初阶,数据库,数据结构和算法系列等,大家有兴趣的可以看一看。 😇😇😇有兴趣的话关注博主一起学习,一起进步吧! 一、MySQL存…

【Excel函数】Vlookup的函数的使用

VLOOKUP(垂直查找)是Microsoft Excel中一种非常有用的函数,用于查找并返回一个特定值所在列的相关信息。它通常用于在大型数据表中查找数据。 以下是VLOOKUP函数的基本语法: (lookup_value, table_array, col_index_num, [range_lookup]) lookup_value:这是您要查找的值或…

rust枚举

一、定义枚举 1.使用enum关键字定义枚举。 语法格式如下 enum enum_name {variant1,variant2,variant3 }例如 enum Fruits {Banana, // 香蕉Pear, // 梨Mandarin, // 橘子Eggplant // 茄子 }2.可以为枚举成员添加属性 enum Book {Papery(u32),Electronic(String), } let bo…

【CNN-FPGA开源项目解析】卷积层01--floatMult16模块

文章目录 (基础)半精度浮点数的表示和乘运算16位半精度浮点数浮点数的乘运算 floatMult16完整代码floatMult16代码逐步解析符号位sign判断指数exponent计算尾数fraction计算尾数fraction的标准化和舍位整合为最后的16位浮点数结果[sign,exponent,fraction] 其他变量宽度表alway…

系统运维工程师

文章目录 引言I 任职要求II Sentinel2.1 安装2.2 开放防火墙端口:III zipkinsee also引言 云效devops部署解决方案: Java应用构建并部署ECS、K8s、SAE、EDAS。 部署微服务的服务器选择。域名劫持:网站被拦截到特定网址,如何解决。你认为初级运维工程师和高级运维工程师的区…

管理公司和管理工作室

有一段时间在写小游戏&#xff0c;最近unity3d 闹出一则荒唐事情&#xff0c;其实说白一点。老贼就要收费这个事情不会妥协。好不容易做了一次坏人。不过说到这一点我们不得不说管理公司这个事情。unity3d在一些新闻公布有7000多员工&#xff0c;这样看起来似乎是不是有点多了&…

HJ90 合法ip 判断合法字符串

题目链接&#xff1a;https://www.nowcoder.com/practice/995b8a548827494699dc38c3e2a54ee9 IPV4地址可以用一个32位无符号整数来表示&#xff0c;一般用点分方式来显示&#xff0c;点将IP地址分成4个部分&#xff0c;每个部分为8位&#xff0c;表示成一个无符号整数&#xff…

Aspose转pdf乱码问题

一、问题描述 ​ 在centos服务器使用aspose.word转换word文件为pdf的时候显示中文乱码(如图)&#xff0c;但是在win服务器上使用可以正常转换 二、问题原因 由于linux服务器缺少对应的字库导致文件转换出现乱码的 三、解决方式 1.将window中字体(c:\windows\fonts)放到linux…

Spring Authorization Server优化篇:Redis值序列化器添加Jackson Mixin,解决Redis反序列化失败问题

前言 在授权码模式的前后端分离的那篇文章中使用了Redis来保存用户的认证信息&#xff0c;在Redis的配置文件中配置的值序列化器是默认的Jdk序列化器&#xff0c;虽然这样也可以使用&#xff0c;但是在Redis客户端中查看时是乱码的(看起来是)&#xff0c;如果切换为Jackson提供…

leetcode刷题 二维数组 八方向

题目描述 输入&#xff1a;board [[0,1,0],[0,0,1],[1,1,1],[0,0,0]] 输出&#xff1a;[[0,0,0],[1,0,1],[0,1,1],[0,1,0]] 题目分析:就是以二维数组某个元素为中心&#xff0c;寻找周围八个方向的元素&#xff0c;按照题目要求修改二维数组元素返回&#xff1b; 拷贝一份二…

Git 代理(Proxy) 配置

某些情况下,我们需要通过代理才能访问特定网络环境下的git资源,git支持代理配置, 支持 http(s), SOCKS4/SOCKS5. HTTP(S) HTTP 代理配置格式如下: git config --global http.proxy http://[proxy]:[port]实际环境下, 其实我们大多数情况下,并不需要全部git资源都需要通过代理…

ElementUI实现登录注册+axios全局配置+CORS跨域

一、搭建项目 1.1 安装 Element-UI 先确保是否安装了vue-cli脚手架工具 !!! 安装vue脚手架可以看看我的上一篇博客 构建好项目后通过npm安装element-ui cd 项目根路径 #进入新建项目的根目录 npm install element-ui -S #安装…

AUTOSAR 面试知识回顾

如果答不上来&#xff0c;就讲当时做了什么 1. Ethernet基础: 硬件接口&#xff1a; ECU到PHY&#xff1a; data 是MII总线&#xff0c; 寄存器控制是SMI总线【MDCMDIO两根线, half duplex】PHY输出(100BASE-T1)&#xff1a; MDI总线&#xff0c;2 wire 【T1: twisted 1 pair …