rust学习-泛型和trait

泛型

Option,Vec,HashMap<K, V>,Result<T, E>等,取函数以减少代码重复的机制

背景

两个函数,不同点只是名称和签名类型

fn largest_i32(list: &[i32]) -> i32 {let mut largest = list[0];for &item in list.iter() {if item > largest {largest = item;}}largest
}fn largest_char(list: &[char]) -> char {let mut largest = list[0];for &item in list.iter() {if item > largest {largest = item;}}largest
}fn main() {let number_list = vec![34, 50, 25, 100, 65];let result = largest_i32(&number_list);println!("The largest number is {}", result);let char_list = vec!['y', 'm', 'a', 'q'];let result = largest_char(&char_list);println!("The largest char is {}", result);
}

重写如下

fn largest<T>(list: &[T]) -> T {let mut largest = list[0];for &item in list.iter() {if item > largest {largest = item;}}largest
}fn main() {let number_list = vec![34, 50, 25, 100, 65];let result = largest(&number_list);println!("The largest number is {}", result);let char_list = vec!['y', 'm', 'a', 'q'];let result = largest(&char_list);println!("The largest char is {}", result);
}// 编译失败,提示受限的类型,因为有的类型没法用
// help: consider restricting type parameter `T`
// fn largest<T: std::cmp::PartialOrd>(list: &[T]) -> T
// binary operation `>` cannot be applied to type `T`
// 具体的解法见下文

结构体的泛型

struct Point<T, U> {x: T,y: U,
}fn main() {let both_integer = Point { x: 5, y: 10 };let both_float = Point { x: 1.0, y: 4.0 };let integer_and_float = Point { x: 5, y: 4.0 };
}

枚举中的泛型

// Some,它存放了一个类型 T 的值、不存在任何值的 None
enum Option<T> {Some(T),None,
}// Result 枚举有两个泛型类型,T 和 E
// Result 有两个成员:Ok,它存放一个类型 T 的值,而 Err 则存放一个类型 E 的值
// 如当成功打开文件的时候,T 对应的是 std::fs::File 类型
// 比如当打开文件出现问题时,E 的值则是 std::io::Error 类型
enum Result<T, E> {Ok(T),Err(E),
}

结构体的泛型

为所有类型的结构体提供方法

struct Point<T> {x: T,y: T,
}// 在为结构体和枚举实现方法时,都可以使用泛型
impl<T> Point<T> {fn x(&self) -> &T {&self.x}
}fn main() {let p = Point { x: 5, y: 10 };println!("p.x = {}", p.x());
}

只为f32提供方法

struct Point<T> {x: T,y: T,
}impl Point<f32> {fn distance_from_origin(&self) -> f32 {(self.x.powi(2) + self.y.powi(2)).sqrt()}
}fn main() {// 打开如下两行,编译报错// method `distance_from_origin` not found for this struct// let p = Point { x: 5, y: 10 };// println!("result={}", p.distance_from_origin());// 如下两行则正常编译let p = Point { x: 5.1, y: 10.2 };println!("result={}", p.distance_from_origin());
}

方法使用了与结构体定义中不同类型的泛型

struct Point<T, U> {x: T,y: U,
}impl<T, U> Point<T, U> {// 泛型参数 V 和 W 声明于 fn mixup 后,因为他们只是相对于方法本身// 删除之后编译失败:cannot find type `V、W` in this scopefn mixup<V, W>(self, other: Point<V, W>) -> Point<T, W> {Point {x: self.x,y: other.y,}}
}fn main() {let p1 = Point { x: 5, y: 10.4 };let p2 = Point { x: "Hello", y: 'c'};let p3 = p1.mixup(p2);println!("p3.x = {}, p3.y = {}", p3.x, p3.y);
}

泛型是否会影响运行时性能

Rust 实现了泛型,使得使用泛型类型参数的代码相比使用具体类型并没有任何速度上的损失
编译时进行泛型代码的单态化:通过填充编译时使用的具体类型,将通用代码转换为特定代码的过程

enum Option_i32 {Some(i32),None,
}enum Option_f64 {Some(f64),None,
}fn main() {// 将泛型定义 Option<T> 展开为 Option_i32 和 Option_f64let integer = Option_i32::Some(5);let float = Option_f64::Some(5.0);
}

trait

trait,定义泛型行为的方法
可以与泛型结合来将泛型限制为拥有特定行为的类型,而不是任意类型
类似于其他语言的接口

示例

为NewsArticle和Tweet实现相同的摘要接口

// trait
pub trait Summary {fn summarize(&self) -> String;
}// 在相同的 lib.rs 里定义了 Summary trait 和 NewsArticle 与 Tweet 类型
// 他们位于同一作用域
pub struct NewsArticle {pub headline: String,pub location: String,pub author: String,pub content: String,
}// 为不同的类型实现不同的summarize
impl Summary for NewsArticle {fn summarize(&self) -> String {format!("{}, by {} ({})", self.headline, self.author, self.location)}
}pub struct Tweet {pub username: String,pub content: String,pub reply: bool,pub retweet: bool,
}// 为不同的类型实现不同的summarize
impl Summary for Tweet {fn summarize(&self) -> String {format!("{}: {}", self.username, self.content)}
}let tweet = Tweet {username: String::from("horse_ebooks"),content: String::from("of course, as you probably already know, people"),reply: false,retweet: false,
};println!("new tweet: {}", tweet.summarize());

trait复用

别人想要用该crate的功能为其自己的库作用域中的结构体实现 Summary trait:
(1)需要将 trait 引入作用域:use aggregator::Summary;
(2)Summary 必须是公有 trait, 其他 crate 才可以实现它,将 pub 置于 trait 之前

只有当 trait 或者要实现 trait 的类型位于 crate 的本地作用域时,才能为该类型实现 trait:
(1)为当前crate 的自定义类型 Tweet 实现如标准库中的 Display trait,Tweet 类型位于本地的作用域
(2)在 当前crate 中为 Vec 实现 Summary,Summary trait 位于 该crate 本地作用域

不能为外部类型实现外部 trait:
(1)不能在 aggregator crate 中为 Vec 实现 Display trait,Display 和 Vec 都定义于标准库中,不在当前作用域

专属词汇:这个限制是被称为 相干性(coherence) 的程序属性的一部分,或者更具体的说是 孤儿规则(orphan rule),忽略即可

trait默认实现

pub trait Summary {fn summarize(&self) -> String {String::from("(Read more...)")}
}

如果想要对 NewsArticle 实例使用这个默认实现,而不是定义一个自己的实现,则可以通过 impl Summary for NewsArticle {} 指定一个空的 impl 块。
为 summarize 创建默认实现并不要求对Tweet 上的 Summary 实现做任何改变。其原因是(重载一个默认实现的语法)与(实现没有默认实现的 trait 方法)的语法一样

trait提供很多接口但只实现部分

pub trait Summary {fn summarize_author(&self) -> String;fn summarize(&self) -> String {format!("(Read more from {}...)", self.summarize_author())}
}// 为了使用这个版本的 Summary
// 只需在实现 trait 时定义 summarize_author
impl Summary for Tweet {fn summarize_author(&self) -> String {format!("@{}", self.username)}
}// 具体实现
let tweet = Tweet {username: String::from("horse_ebooks"),content: String::from("of course, as you probably already know, people"),reply: false,retweet: false,
};
println!("1 new tweet: {}", tweet.summarize());

trait作参数-impl trait

使用 trait 来接受多种不同类型的参数

// 对于 item 参数,指定 impl 关键字和 trait 名称,而不是具体的类型
// 该参数支持任何实现了指定 trait 的类型
pub fn notify(item: impl Summary) {// 在 notify 函数体中,可以调用任何来自 Summary trait 的方法// 可以传递任何 NewsArticle 或 Tweet 的实例来调用 notifyprintln!("Breaking news! {}", item.summarize());
}

trait作参数-trait Bound

impl Trait 很方便,适用于短小的例子
trait bound 则适用于更复杂的场景

// trait bound 与泛型参数声明在一起,位于尖括号中的冒号后面
pub fn notify<T: Summary>(item: T) {println!("Breaking news! {}", item.summarize());
}

获取两个实现了 Summary 的参数

// 适用于 item1 和 item2 允许是不同类型的情况(只要它们都实现了 Summary)
pub fn notify(item1: impl Summary, item2: impl Summary) {// 强制它们都是相同类型
// 泛型 T 被指定为 item1 和 item2 的参数限制
// 如此传递给参数 item1 和 item2 值的具体类型必须一致。
pub fn notify<T: Summary>(item1: T, item2: T) {

指定多个 trait bound

既要显示 item 的格式化形式,也要使用 summarize 方法

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

简化trait bound

每个泛型有其自己的 trait bound

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

返回值是trait类型

和go的interface太像了,但又像阉割版的
调用方并不知情返回的类型,只适用于返回单一类型的情况

fn returns_summarizable() -> impl Summary {Tweet {username: String::from("horse_ebooks"),content: String::from("of course, as you probably already know, people"),reply: false,retweet: false,}
}

糟糕的例子

fn returns_summarizable(switch: bool) -> impl Summary {if switch {NewsArticle {headline: String::from("Penguins win the Stanley Cup Championship!"),location: String::from("Pittsburgh, PA, USA"),author: String::from("Iceburgh"),content: String::from("The Pittsburgh Penguins once again are the besthockey team in the NHL."),}} else {Tweet {username: String::from("horse_ebooks"),content: String::from("of course, as you probably already know, people"),reply: false,retweet: false,}}
}

最开始例子的修复

使用泛型参数 trait bound 来指定所需的行为
在 largest 函数体中想要使用大于运算符(>)比较两个 T 类型的值。这个运算符被定义为标准库中 trait std::cmp::PartialOrd 的一个默认方法。

修改函数签名

fn largest<T: PartialOrd>(list: &[T]) -> T {// 报错如下
// cannot move out of type `[T]`, a non-copy slice
// i32 和 char 这样的类型是已知大小的并可以储存在栈上,所以他们实现了 Copy trait
// 将 largest 函数改成使用泛型后,现在 list 参数的类型就有可能是没有实现 Copy trait
// 意味着可能不能将 list[0] 的值移动到 largest 变量中

解决方法1

为了只对实现了 Copy 的类型调用这些代码
可以在 T 的 trait bounds 中增加 Copy

fn largest<T: PartialOrd + Copy>(list: &[T]) -> T {let mut largest = list[0];for &item in list.iter() {if item > largest {largest = item;}}largest
}fn main() {let number_list = vec![34, 50, 25, 100, 65];let result = largest(&number_list);println!("The largest number is {}", result);let char_list = vec!['y', 'm', 'a', 'q'];let result = largest(&char_list);println!("The largest char is {}", result);
}

解决方法2

不希望限制 largest 函数只能用于实现了 Copy trait 的类型
可以在 T 的 trait bounds 中指定 Clone 而不是 Copy
克隆 slice 的每一个值使得 largest 函数拥有其所有权
使用 clone 函数意味着对于类似 String 这样拥有堆上数据的类型
会潜在的分配更多堆上空间
而堆分配在涉及大量数据时可能会相当缓慢

解决方式3

返回在 slice 中 T 值的引用
如果将函数返回值从 T 改为 &T 并改变函数体使其能够返回一个引用
将不需要任何 Clone 或 Copy 的 trait bounds 而且也不会有任何的堆分配

trait Bound 有条件地实现方法

use std::fmt::Display;struct Pair<T> {x: T,y: T,
}impl<T> Pair<T> {fn new(x: T, y: T) -> Self {Self {x,y,}}
}// 只有那些为 T 类型实现了 PartialOrd trait (来允许比较) 和 Display trait (来启用打印)的 Pair<T> 
// 才会实现 cmp_display
impl<T: Display + PartialOrd> Pair<T> {fn cmp_display(&self) {if self.x >= self.y {println!("The largest member is x = {}", self.x);} else {println!("The largest member is y = {}", self.y);}}
}

对任何实现了特定 trait 的类型有条件地实现 trait

blanket implementations,广泛用于 Rust 标准库
标准库为任何实现了 Display trait 的类型实现了 ToString trait
对任何实现了 Display trait 的类型调用由 ToString 定义的 to_string 方法
// 某个行为依赖于另一个行为

impl<T: Display> ToString for T {// --snip--
}

使用场景

let s = 3.to_string();

总结

动态类型语言中如果尝试调用一个类型并没有实现方法,运行时报错
Rust 将这些错误移动到编译时,甚至在代码能运之前就强迫修复错误
无需编写运行时检查行为的代码,因为在编译时就已经检查过了
相比其他不愿放弃泛型灵活性的语言有更好的性能

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

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

相关文章

【python】动态规划:牛客网HJ24 合唱队

描述 N 位同学站成一排&#xff0c;音乐老师要请最少的同学出列&#xff0c;使得剩下的 K 位同学排成合唱队形。 设KK位同学从左到右依次编号为 1&#xff0c;2…&#xff0c;K &#xff0c;他们的身高分别为T1,T2,…,TKT1​,T2​,…,TK​ &#xff0c;若存在i(1≤i≤K)i(1≤i…

【数字IC前端笔试真题精刷(2021)】大疆——数字芯片开发工程师A/B卷

声明:本专栏所收集的数字IC笔试题目均来源于互联网,仅供学习交流使用。如有侵犯您的知识产权,请及时与博主联系,博主将会立即删除相关内容。 笔试时间:2021年A/B卷 题目类型:(只列了选择题) 大疆芯片开发岗A卷 文章目录 一、单选题 / 多选题1.(单选)关于流水线设计的…

C++并发编程(6):单例模式、once_flag与call_once、call_once实现单例

单例模式 参考博客 【C】单例模式&#xff08;饿汉模式、懒汉模式&#xff09; C单例模式总结与剖析 饿汉单例模式 C实现 C单例模式(饿汉式) 设计模式&#xff08;Design Pattern&#xff09;是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结 &#xff0c;一共…

行为型模式 - 策略模式

概述 先看下面的图片&#xff0c;我们去旅游选择出行模式有很多种&#xff0c;可以骑自行车、可以坐汽车、可以坐火车、可以坐飞机。 作为一个程序猿&#xff0c;开发需要选择一款开发工具&#xff0c;当然可以进行代码开发的工具有很多&#xff0c;可以选择Idea进行开发&…

Web 3.0时代,重塑教育与学习方式的可能性

随着科技的快速发展和互联网的普及&#xff0c;教育领域也面临着巨大的机遇和挑战。Web 3.0时代的到来为教育与学习方式带来了全新的可能性。在这个数字化时代&#xff0c;我们可以探索和利用Web 3.0技术&#xff0c;重塑教育的方式&#xff0c;提供更个性化、互动性和灵活性的…

在Illustrator中创建 3D 冰淇淋模型对象

推荐&#xff1a; NSDT场景编辑器助你快速搭建可二次开发的3D应用场景 一旦你学会了如何在Illustrator中制作一个对象3D&#xff0c;你可以前往Envato Elements&#xff0c;在那里你可以找到大量的3D设计来激发你的灵感。这个基于订阅的市场拥有超过 2&#xff0c;000 个 Illus…

php实现站群软件权限管理功能示例

1.管理员页面RBAC.php <!DOCTYPE html> <html> <head> <meta charset"UTF-8"> <title>权限管理</title> <script src"bootstrap/js/jquery-1.11.2.min.js"></script> </head>…

项目名称:智能家居边缘网关项目

一&#xff0c;项目介绍 软件环境: C语言 硬件环境: STM32G030C8TX单片机开发板 开发工具: Linux平台GCC交叉编译环境以及ukeil (1)边缘网关概念 边缘网关是部署在网络边缘侧的网关&#xff0c;通过网络联接、协议转换等功能联接物理和数字世界&#xff0c;提供轻量化的联接管…

通过new FormData提交简单数据

通过new FormData提交简单数据 效果示例图代码 效果示例图 代码 <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title><style type"text/css">* {padding: 0px;margin: 0px;box-sizing: border-…

SpringBoot整合PostgreSQL教程

主要描述如何优雅的整合postgresql。本文略去如何安装pgsql的过程&#xff0c;详情可参考其他文章。 文章目录 postgresql简介整合postgresql整合mybatis整合mybatis-plus postgresql简介 与mysql一样也是开源的关系型数据库&#xff0c;同时还支持NoSql的文档型存储。在某些方…

费尔法克斯水务通过使用 Liquid UI 移动化和定制 SAP PM 来提高收入和数据完整性

背景 费尔法克斯水务是北弗吉尼亚州地区领先的水县。它是华盛顿特区大都会区的三大供水商之一。它每天为近171万居民提供2.<>亿加仑的水。它渴望坚持其愿景&#xff0c;即保持以客户为中心&#xff0c;同时帮助维持该地区的高质量生活和经济状况。 挑战 由于桌面系统&…

【Ceph的部署】

目录 一、基于 ceph-deploy 部署 Ceph 集群1、Ceph 生产环境推荐&#xff1a;2、Ceph 环境规划3、环境准备1、关闭 selinux 与防火墙2、根据规划设置主机名3、配置 hosts 解析4、安装常用软件和依赖包5、在 admin 管理节点配置 ssh 免密登录所有节点6、为每一个服务器配置时间同…

写给后端开发的『vue3』请求后端接口

本文分享一下在vue3前端项目中请求后端接口获取数据。比较简单&#xff0c;内容如下&#xff1a; 1、使用axios请求后端接口 首先npm install axios&#xff0c;添加axios依赖&#xff0c;就靠它来请求后端接口了&#xff0c;基本等同于使用jquery发ajax。 # src/main.js i…

每天一点Python——day58

#第五十八天 集合间的关系&#xff1a; 类似于数学中学到的集合一样&#xff0c;关系差不多&#xff0c;譬如相等&#xff0c;子集&#xff0c;交集 如图所示&#xff1a;#①两个集合是否相等&#xff1a;运用运算符【等号】或者运算符&#xff01;【不等号】进行判断 #例&…

【面试问题11】

1.Filter 和interceptor区别 filter是tomcat的规范,在请求前对request对象进行拦截,执行相关的过滤dofilter,例如url拦截请求静态文件,添加请求参数,权限检查,敏感字符检查等,请求后会再执行一次dofilter。区别,1. filter只tomcat规范,interceptor是spring规范。2.执行…

MySQL备份/恢复、索引、视图简述与练习

文章目录 MYSQL备份&#xff1a;物理备份&#xff1a;逻辑备份&#xff1a; 索引&#xff1a;原理&#xff1a;优缺点&#xff1a; 视图&#xff1a;什么是视图&#xff1a;作用&#xff1a;优点&#xff1a; 备份与恢复练习题&#xff1a;创库,建表&#xff1a;插入数据&#…

python系列教程210——嵌套lambda

朋友们&#xff0c;如需转载请标明出处&#xff1a;https://blog.csdn.net/jiangjunshow 声明&#xff1a;在人工智能技术教学期间&#xff0c;不少学生向我提一些python相关的问题&#xff0c;所以为了让同学们掌握更多扩展知识更好地理解AI技术&#xff0c;我让助理负责分享…

人工智能大语言模型微调技术:SFT 监督微调、LoRA 微调方法、P-tuning v2 微调方法、Freeze 监督微调方法

人工智能大语言模型微调技术&#xff1a;SFT 监督微调、LoRA 微调方法、P-tuning v2 微调方法、Freeze 监督微调方法 1.SFT 监督微调 1.1 SFT 监督微调基本概念 SFT&#xff08;Supervised Fine-Tuning&#xff09;监督微调是指在源数据集上预训练一个神经网络模型&#xff…

HTML5 Canvas API制作一个简单的猜字单机游戏

这篇文章主要介绍了借助HTML5 Canvas API制作一个简单的猜字单机游戏的实例分享,游戏中每局会自动生成一个字母,玩家按键盘来猜测该字母是哪一个,需要的朋友可以参考下 HTML代码 <!doctype html> <html lang"en"> <head> <met…

听GPT 讲K8s源代码--pkg(六)

pkg/kubelet/cm 目录是 Kubernetes 源代码中的一个目录&#xff0c;包含了 kubelet 组件中的 ConfigMap 相关代码。 在 Kubernetes 中&#xff0c;ConfigMap 是一种用于存储非机密数据的 API 对象类型&#xff0c;它可以用来存储配置信息、环境变量、命令行参数等等。 kubelet …