泛型、Trait和生命周期(10)

泛型、Trait和生命周期

    • 1.泛型数据类型
      • 1.1在函数定义中使用泛型
      • 1.2结构体定义中的泛型
      • 1.3枚举定义中的泛型
      • 1.4方法定义中的泛型
      • 1.5泛型代码的性能
    • 2.Trait: 定义共同行为
      • 2.1定义trait
      • 2.2为类型实现trait
      • 2.3默认实现
      • 2.4trait作为参数
      • 2.5Trait Bound语法
      • 2.6通过 + 指定多个trait bound
      • 2.7通过where 简化 trait bound
      • 2.8返回实现了trait的类型
    • 3.生命周期确保引用有效
      • 3.1生命周期避免了悬垂引用
      • 3.2借用检查器
      • 3.3函数中的泛型生命周期
      • 3.4生命周期注解语法
      • 3.5函数签名中的生命周期注解
        • 注意事项
      • 3.6深入理解生命周期
      • 3.7结构体定义中的生命周期注解
      • 3.8生命周期省略
        • 1.采用三条规则来判断引用何时不需要明确的注解
      • 3.9方法定义中的生命周期注解
      • 3.10静态生命周期
      • 3.11结合泛型类型参数、trait bounds和生命周期

1.泛型数据类型

1.1在函数定义中使用泛型

  • 两个函数,不同点只是名称和签名类型
fn largest_i32(list: &[i32]) -> &i32 {let mut largest = &list[0];for item in list {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);
}
  • 泛型初试

/*errorfn largest<T: std::cmp::PartialOrd>(list: &[T]) -> &T {}泛型T没实现PartialOrd,标准库 i32,char 都实现了PartialOrd所以,泛型T: PartialOrd + Copy,可以解决error: the trait bound `T: std::cmp::PartialOrd` is not satisfied*/
// fn largest<T>(list: &[T]) -> &T {
//     let mut largest = &list[0];//     for item in list {
//         if item > largest {
//             largest = item;
//         }
//     }//     largest
// }fn largest<T: PartialOrd + Copy>(list: &[T]) -> &T {let mut largest = &list[0];for item in list {if item > largest {largest = item;}}largest
}fn main() {let result = largest(&number_list);println!("The largest number is {}", result);let result = largest(&char_list); println!("The largest char is {}", result);
}

1.2结构体定义中的泛型

struct Point<T> {x: T,y: T,
}
// 通过T U泛型,可以指定x y为不同的数据类型
struct Points<T, U> {x: T,y: U,
}fn main(){let p1 = Point{x: 1, y: 2};println!("({},{})", p1.x, p1.y);let p2 = Points{x: 1.0, y: 52};println!("({},{})", p2.x, p2.y);
}

1.3枚举定义中的泛型

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

1.4方法定义中的泛型

  • 方法使用了与结构体定义中不同类型的泛型
struct Point<T, U>{x: T,y: U,
}
impl<T, U> Point<T, U>{fn x(&self) -> &T{&self.x}fn y(&self) -> &U{&self.y}fn 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);// 5 c
}

1.5泛型代码的性能

  • Rust 通过在编译时进行泛型代码的 单态化(monomorphization)来保证效率。单态化是一个通过填充编译时使用的具体类型,将通用代码转换为特定代码的过程

2.Trait: 定义共同行为

  • trait 类似于其他语言中的常被称为 接口(interfaces)的功能

2.1定义trait

  • 一个类型的行为由其可供调用的方法构成
  • 如果可以对不同类型调用相同的方法的话,这些类型就可以共享相同的行为
  • trait 定义是一种将方法签名组合起来的方法,目的是定义一个实现某些目的所必需的行为的集合
pub trait Summary {fn summarize(&self) -> String;
}

2.2为类型实现trait

  • 在NewArticle和Tweet类型上实现 Summary trait
// lib.rs
pub trait Summary {fn summarize(&self) -> String;
}pub struct NewsArticle {pub headline: String,pub location: String,pub author: String,pub content: String,
}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,
}impl Summary for Tweet {fn summarize(&self) -> String {format!("{}: {}", self.username, self.content)}
}
// main.rsuse aggregate::{Summary, Tweet};fn main() {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());
}

2.3默认实现

  • trait中的某些或全部方法提供默认的行为,而不是在每个类型的每个实现中都定义自己的行为是很有用的
// lib.rs
pub trait Summary {fn summarize(&self) -> String {String::from("(Read more...)")}
}

2.4trait作为参数

pub fn notify(item: &impl Summary){println!("Breaking news! {}", item.summarize());
}

2.5Trait Bound语法

// Trait Bound语法
pub fn notify<T: Summary>(item: &T) {println!("Breaking news! {}", item.summarize());}// 非Trait Bound语法
pub fn notify(item: &impl Summary, item2: &impl Summary){}

2.6通过 + 指定多个trait bound

pub fn notify (item: &(impl Summary + Display)){}// + 语法也适用于泛型的trait boundpub fn notify<T: Summary + Display> (item: &T){}

2.7通过where 简化 trait bound

fn some_function<T: Display + Clone, U: Clone + Debug>(t: &T,u: &U) -> i32{}// 通过where简化
fn some_function<T,U>(t: &T,u: &U) -> i32
whereT: Display + Clone,U: Clone + Debug,
{}

2.8返回实现了trait的类型

  • 可以通过返回值中使用 impl trait 语法,来返回实现了某个trait的类型
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,}
}

3.生命周期确保引用有效

3.1生命周期避免了悬垂引用

  • 生命周期的主要目标是避免悬垂引用,后者会导致程序引用了非预期引用的数据
fn main() {let r;{let x = 5;r = &x; // error,   borrowed value does not live long enough}// 此处,x就释放内存println!("r: {}", r);
}

3.2借用检查器

  • rust编译器有一个借用检查器,比较作用域来确保所有的借用都是有效的
fn main() {// let r;// {//     let x = 5;//     r = &x;// }// println!("r: {}", r);let y = 5;let e = &y;println!("e: {}", e);//  e: 5
}

3.3函数中的泛型生命周期

// 借用检查器自身同样也无法确定,因为它不知道 x 和 y 的生命周期
fn main() {  let string1 = String::from("abcd");  let string2 = "xyz";  let result = longest(&string1, string2);   println!("The longest string is {}", result);  
}
fn longest(a: &str, b: &str) -> &str {  if a.len() > b.len() {  a  } else {  b  }  
}  

3.4生命周期注解语法

  • 生命周期注解并不改变任何引用的生命周期的长短
  • 生命周期参数名称必须以撇号( ’ )开头,其名称通常全是小写,类似于泛型其名称非常短
  • 大多数人使用 'a 作为第一个生命周期注解
&i32			//引用
&'a i32			//带有显式生命周期的引用
&'a mut i32		//带有显式生命周期的可变引用

3.5函数签名中的生命周期注解

  • 为了在函数签名中使用什么周期注解,需要在函数名和参数列表间的尖括号中声明泛型生命周期参数
  • 当在函数中使用生命周期注解时,这些注解出现在函数签名中,而不存在于函数体中的任何代码中
  • 通过在函数签名中指定生命周期参数时,我们并没有改变任何传入值或返回值的生命周期,而是指出任何不满足这个约束条件的值都将被借用检查器拒绝
fn main() {  let string1 = String::from("abcd");  let string2 = "xyz";  let result = longest(&string1, string2);   println!("The longest string is {}", result);  
}
fn longest<'a>(a: &'a str, b: &'a str) -> &'a str {  if a.len() > b.len() {  a  } else {  b  }  
}  
注意事项

fn main() {  // string2 在作用域结束前有效let string1 = String::from("abcd");  {let string2 = "xyz";  let result = longest(&string1, string2);   println!("The longest string is {}", result); }// let string1 = String::from("abcd"); // let result; // {//     let string2 = "xyz";  //     result = longest(&string1, string2);   // }// // string2 需要知道外部作用域结束都是有效的,longest函数返回的是一个引用,所以result需要知道string2的作用域结束// // 否则编译器会报错// println!("The longest string is {}", result); }
fn longest<'a>(a: &'a str, b: &'a str) -> &'a str {  if a.len() > b.len() {  a  } else {  b  }  
}  

3.6深入理解生命周期

  • 指定生命周期参数的正确方式依赖函数实现的具体功能
  • 返回的引用 没有 指向任何一个参数,那么唯一的可能就是它指向一个函数内部创建的值
  • 它将会是一个悬垂引用,因为它将会在函数结束时离开作用域
fn longest<'a>(x: &str, y: &str) -> &'a str {let result = String::from("really long string");result.as_str()
}

3.7结构体定义中的生命周期注解

  • 定义的结构体全都包含拥有所有权的类型
  • 定义包含引用的结构体,不过这需要为结构体定义中的每一个引用添加生命周期注解

// 定义包含引用的结构体,需要为结构体定义中的每一个引用添加生命周期注解
struct ImportantExcerpt<'a> {part: &'a str,
}fn main() {let novel = String::from("Call me Ishmael. Some years ago...");let first_sentence = novel.split('.').next().expect("Could not find a '.'");let i = ImportantExcerpt { part: first_sentence };println!("{}", i.part);
}

3.8生命周期省略

  • 函数或方法的参数的生命周期被称为 输入生命周期
  • 返回值的生命周期被称为 输出生命周期
1.采用三条规则来判断引用何时不需要明确的注解
  • 第一条规则是编译器为每一个引用参数都分配一个生命周期参数
  • 第二条规则是如果只有一个输入生命周期参数,那么它被赋予所有输出生命周期参数:fn foo<'a>(x: &'a i32) -> &'a i32
  • 第三条规则是如果方法有多个输入生命周期参数并且其中一个参数是 &self 或 &mut self,说明是个对象的方法 (method),那么所有输出生命周期参数被赋予 self 的生命周期
  • 编译器检查完这条规则后仍然存在没有计算出生命周期的引用,编译器将会停止并生成错误

3.9方法定义中的生命周期注解

impl<'a> ImportantExcerpt<'a> {fn announce_and_return_part(&self, announcement: &str) -> &str {println!("Attention please: {announcement}");self.part}
}

3.10静态生命周期

  • 'static ,其生命周期能够存活于整个程序期间

3.11结合泛型类型参数、trait bounds和生命周期

use std::fmt::Display;fn longest_with_an_announcement<'a, T>(x: &'a str, y: &'a str, ann: T) -> &'a str
whereT: Display,
{println!("Announcement! {}", ann);if x.len() > y.len() {x} else {y}
}

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

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

相关文章

golang语言 .go文件版本条件编译,xxx.go文件指定go的编译版本必须大于等于xxx才生效的方法, 同一个项目多个go版本文件共存方法

在go语言中&#xff0c;我们不关是可以在编译时指定版本&#xff0c; 在我们的xxx.go文件中也可以指定go的运行版本&#xff0c;即 忽略go.mod中的版本&#xff0c;而是当当前的go运行版本达到指定条件后才生效的xxx.go文件。 方法如下&#xff1a; 我们通过在xxx.go文件的头部…

vue3访问URL添加session变量

本文主要讲了我们在访问网址时候&#xff0c;为了实现一些免登录等效果&#xff0c;需要在会话Session中添加一些变量&#xff0c;已实现特定效果。 关键代码在于蓝色这部分&#xff1a; <script setup> import { ref } from vue import axios from axios const loginUrl…

java小白针对大数据多表联查的一些小思路,帮助新手学习

假设我的member_user里面有352599条数据&#xff0c;gp_project里面有1211974条数据 SELECT gp.*, mu.linkmanName , mu.linkmanPhone, mu.legalPersonName, mu.legalPersonPhone, mu.address, mu.registerArea FROM gp_project gp LEFT JOIN member_user mu ON mu.supplierId…

通过“微软蓝屏”事件对网络安全的思考

近日&#xff0c;一次由微软视窗系统软件更新引发的全球性“微软蓝屏”事件&#xff0c;不仅成为科技领域的热点新闻&#xff0c;更是一次对全球IT基础设施韧性与安全性的深刻检验。这次事件&#xff0c;源于美国电脑安全技术公司“众击”提供的一个带有“缺陷”的软件更新&…

C++在高性能计算与游戏开发中的应用

在追求极致性能和用户体验的今天&#xff0c;C凭借其高效的执行效率和强大的控制能力&#xff0c;在高性能计算&#xff08;HPC&#xff09;和游戏开发领域大放异彩。无论是处理大规模数据集的并行计算&#xff0c;还是构建复杂、逼真的游戏世界&#xff0c;C都展现出了无可比拟…

04 HTML CSS JavaScript

文章目录 HTML1、HTML介绍2、快速入门3、基础标签4、图片、音频、视频标签5、超链接标签6、列表标签7、表格标签8、布局标签9、 表单标签 CSS1、 概述2、 css 导入方式3、 css 选择器4、 css 属性 JavaScript1、JavaScript简介2、JavaScript引入方式3、JavaScript基础语法4、Ja…

【C++刷题】优选算法——分治

颜色分类 void sortColors(vector<int>& nums) {int left -1, right nums.size();for (int i 0; i < right;) {if (nums[i] 0) {swap(nums[left], nums[i]);} else if (nums[i] 2) {swap(nums[--right], nums[i]);} else {i;}} }排序数组 // 快速排序 void…

Potree在web端显示大型点云模型文件

一、克隆项目代码&#xff08;准备好上网工具&#xff0c;得先有node.js npm 环境&#xff09; git clone https://github.com/potree/potree.git二、依赖安装&#xff08;换淘宝镜像能快一些&#xff09; cd potree npm install三、运行 npm start四、使用样例 打开浏览器…

44、PHP 实现数据流中的中位数(含源码)

题目&#xff1a; PHP 实现数据流中的中位数 描述&#xff1a; 如何得到一个数据流中的中位数&#xff1f; 如果从数据流中读出奇数个数值&#xff0c;那么中位数就是所有数值排序之后位于中间的数值。 如果从数据流中读出偶数个数值&#xff0c;那么中位数就是所有数值排序之…

【Spring】SpringRetry重试机制和Spring异步任务发送操作结合应用场景实操,通俗易懂

平时调用一些第三方接口或者内部接口&#xff0c;可能出现处理异常或者超时或者意外因素&#xff0c;我们可以使用重试机制来为用户提高体验。 1.引用依赖 <dependency><groupId>org.springframework.retry</groupId><artifactId>spring-retry</a…

【Qt】文字换行

目录 Qt换行方式一方法二 参考文章 在Qt控件中经常遇到文字超出文本框&#xff0c;因此介绍几种换行方式 Qt换行 方式一 QLabel 设置setWordWrap(true);实现换行。但此方法对于多语言文本不太友好&#xff0c;可以在超出文本框时添加省略符&#xff0c;方法如下 //要显示的超…

单片机学习(18)--红外遥控器

红外遥控器 17.1红外遥控的基础知识1.红外遥控简介2.硬件电路3.基本发送和接收4.NEC编码5.遥控器键码6.51单片机的外部中断7.外部中断寄存器 17.2红外遥控的程序代码1.红外遥控&#xff08;1&#xff09;工程目录&#xff08;2&#xff09;main.c函数&#xff08;3&#xff09;…

vue 实战 tab标签页+el-card+流式布局+异步接口调用

<template><div><!-- 布局按钮 --><el-button click"dialogVisible true">布局配置查看</el-button><!-- 布局配置对话框 --><el-dialog :visible.sync"dialogVisible" title"布局配置查看" width"…

Invalid bound statement (not found)

Invalid bound statement (not found) 首先申明的是这个错误一般是使用mybatis方法没有找到或者参数不匹配等原因造成的&#xff01; 原本项目是使用eclipse运行&#xff0c;导入到idea之后&#xff0c;项目启动就报错 …Invalid bound statement (not found)… 解决办法&#…

Python 爬虫(爬取百度翻译的数据)

前言 要保证爬虫的合法性&#xff0c;可以从以下几个方面着手&#xff1a; 遵守网站的使用条款和服务协议&#xff1a;在爬取数据之前&#xff0c;仔细阅读目标网站的相关规定。许多网站会在其 robots.txt 文件中明确说明哪些部分可以爬取&#xff0c;哪些不可以。 例如&…

中电金信:AI数据服务

01 方案简介 AI数据服务解决方案为泛娱乐、电子商务、交通出行等行业提供数据处理、数据分析、AI模型训练等服务&#xff0c;通过自主研发的IDSC自动化数据服务平台与客户业务流程无缝衔接&#xff0c;实现超低延时的实时数据处理支持。 02 应用场景 智能医疗&#xff1a; 通…

深入浅出mediasoup—通信框架

libuv 是一个跨平台的异步事件驱动库&#xff0c;用于构建高性能和可扩展的网络应用程序。mediasoup 基于 libuv 构建了包括管道、信号和 socket 在内的一整套通信框架&#xff0c;具有单线程、事件驱动和异步的典型特征&#xff0c;是构建高性能 WebRTC 流媒体服务器的重要基础…

《javaEE篇》--单例模式详解

目录 单例模式 饿汉模式 懒汉模式 懒汉模式(优化) 指令重排序 总结 单例模式 单例模式属于一种设计模式&#xff0c;设计模式就好比是一种固定代码套路类似于棋谱&#xff0c;是由前人总结并且记录下来我们可以直接使用的代码设计思路。 单例模式就是&#xff0c;在有…

升级python版本

参考 https://blog.51cto.com/u_15579956/10397535 python3 main.py

聚焦保险行业客户经营现状,概述神策数据 CJO 解决方案

触点红利时代&#xff0c;企业的经营需求从「深度的用户行为分析」转变为「个性化、全渠道一致的客户体验」。客户旅程编排&#xff08;Customer Journey Orchestration&#xff0c;简称 CJO&#xff09;从体验出发&#xff0c;关注客户需求、感受和满意度&#xff0c;能够帮助…