13.泛型、trait和生命周期(下)

2024-06-16 18:28
文章标签 13 泛型 生命周期 trait

本文主要是介绍13.泛型、trait和生命周期(下),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

  • 6. 生命周期与引用有效性
    • 6.1 生命周期引入
    • 6.2 得到长度最大的String值
    • 6.3 生命周期标注语法
      • 1)说明
      • 2)普通标注示例
      • 3)函数参数中的生命周期标注
    • 6.4 深入理解生命周期
    • 6.5 结构体定义中的生命周期标注
    • 6.6 生命周期省略
  • 6.7 方法定义中的生命周期标注
    • 6.8 静态生命周期
    • 6.9 结合泛型类型参数、trait bounds 和生命周期

6. 生命周期与引用有效性

  • Rust 中的每一个引用都有其生命周期(lifetime);
  • 大部分时候生命周期是隐含并可以推断的;
  • 当出现引用的生命周期以一些不同方式相关联的情况,需要使用泛型生命周期参数来注明他们的关系;

6.1 生命周期引入

fn main() {let r;{let x = 5;r = &x;}println!("r: {}", r);
}

这段代码报错
在这里插入图片描述

  • x 在到达第 7 行内部作用域结束时就离开了作用域,r的引用值也就不存在了;

6.2 得到长度最大的String值

下面的代码会得到传入两个参数中最长的String,为了避免所有权的占用,代码采用引入的方法传参

fn longest(src1: &str, src2: &str) -> &str {if src1.len() > src2.len() {src1}else{src2}
}fn main() {let string1 = String::from("abcd");let string2 = "xyz";let result = longest(string1.as_str(), string2);println!("The longest string is {}", result);
}
  • 然而,编译时提示存在生命周期的错误
    在这里插入图片描述

6.3 生命周期标注语法

1)说明

  • 生命周期标注并不改变任何引用的生命周期的长短;
  • 指定了泛型生命周期后函数也能接受任何生命周期的引用;
  • 生命周期标注描述了多个引用生命周期相互的关系,而不影响其生命周期;
  • 生命周期参数名称必须以撇号(')开头,其名称通常全是小写, 'a 是大多数人默认使用的名称;
  • 生命周期参数标注位于引用的 & 之后,并有一个空格来将引用类型与生命周期标注分隔开;

2)普通标注示例

&i32       		 	// 引用
&'a i32     		// 带有显式生命周期的引用
&'a mut i32 		// 带有显式生命周期的可变引用

3)函数参数中的生命周期标注

fn longest<'a>(src1: &'a str, src2: &'a str) -> &'a str {if src1.len() > src2.len() {src1}else{src2}
}
  • 生命周期参数需要声明在函数名和参数列表之间的尖括号中;
  • 这里说明关于参数中的引用和返回值之间的限制是他们都必须拥有相同的生命周期;
  • 生命周期标记仅仅出现在函数的参数中,不存在于函数体中的任何代码中;
  • 泛型生命周期’a的具体生命周期等同于src1 和src2的生命周期中较小的那一个;

传递拥有不同具体生命周期的引用来限制longest函数的使用。

fn main() {let string1 = String::from("long string is long");{let string2 = String::from("xyz");let result = longest(string1.as_str(), string2.as_str());println!("The longest string is {}", result);    //The longest string is long string is long}
}
  • string1直到main函数结束之前都是有效的;
  • string2只在最里面的{}内才有效,而result则引用了直到内部作用域结束都是有效的值;

更改代码再尝试

fn main() {let string1 = String::from("long string is long");let result;{let string2 = String::from("xyz");result = longest(string1.as_str(), string2.as_str());}println!("The longest string is {}", result);
}

无法通过编译
在这里插入图片描述

  • 第18行的result变量是有效的;
  • longest函数的参数和返回值使用了相同的生命周期参数;
  • 因此,string2需要直到main函数结束都前都是有效的;

6.4 深入理解生命周期

指定生命周期参数的正确方式依赖函数实现的具体功能;

fn longest<'a>(x: &'a str, y: &str) -> &'a str {x
}
  • longest总是返回第一个参数,因此不需要为参数y指定生命周期;

从函数返回一个引用,返回值的生命周期参数需要与一个参数的生命周期参数相匹配

fn longest<'a>(x: &str, y: &str) -> &'a str {let result = String::from("really long string");result.as_str()
}

编译报错
在这里插入图片描述

  • 返回的引用没有指向任何一个参数;
  • 因此它指向的是一个内部创建的值,这就会造成悬垂引用,因此无法编译通过;

6.5 结构体定义中的生命周期标注

  • 可以定义包含引用的结构体;
  • 需要为结构体定义中的每一个引用添加第一期周期标注;
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 };
}
  • ImportantExcerpt结构体中存放了一个字符串切片类型(&str ),因此需要声明生命周期;
  • 在结构体名称后面的尖括号中声明泛型生命周期参数;
  • main函数创建了一个ImportantExcerpt实例,它存放了变量novel的引用;
  • novel在ImportantExcerpt之前已经存在,且在ImportantExcerpt离开作用域之后novel都不会离开作用域;
  • 因此能通过编译;

6.6 生命周期省略

  • 每一个引用都有一个生命周期;
  • 需要为那些使用了引用的函数或结构体指定生命周期;

然而,下面的代码能通过编译器检查

fn first_word(s: &str) -> &str {let bytes = s.as_bytes();for (i, &item) in bytes.iter().enumerate() {if item == b' ' {return &s[0..i];}}&s[..]
}
  • 函数定义了一个参数和返回值都是引用却没有使用生命周期标注的函数;
  • 这是一种生命周期省略规则,省略规则只需要提示出错的时候补全生命周期标注即可;
  • 规则适用于fn定义以及fmpl块;

引用不需要标注生命周期的规则

  • 函数或方法的参数的生命周期被称为输入生命周期;
  • 返回值的生命周期被称为输出生命周期;

适用于输入生命周期的规则

规则一:每一个是引用的参数都有它自己的生命周期参数。
例如:

fn foo<'a>(x: &'a i32){}
fn foo<'a, 'b>(x: &'a i32, y: &'b i32){}

因此

fn first_word(s: &str) -> &str{}

被当作

fn first_word<'a>(s: &'a str) -> &str{}

适用于输出生命周期的规则

规则一:如果只有一个输入生命周期参数,那么它被赋予所有输出生命周期参数

例如

fn foo<'a>(x: &'a i32) -> &'a i32

因此

fn first_word(s: &str) -> &str{}

被当作

fn first_word<'a>(s: &'a str) -> &'a str {

规则二:如果方法有多个输入生命周期参数并且其中一个参数是&self或&mut self,说明是个对象的方法, 那么所有输出生命周期参数被赋予 self 的生命周期

6.7 方法定义中的生命周期标注

  • 为带有生命周期的结构体实现方法时,其语法依然类似泛型类型参数的语法;
  • 声明和使用生命周期参数的位置依赖于生命周期参数是否同结构体字段或方法参数和返回值相关;
  • 实现方法时,结构体字段的生命周期必须总是在 impl 关键字之后声明并在结构体名称之后被使用;
  • impl 块里的方法签名中,引用可能与结构体字段中的引用相关联,也可能是独立的
struct ImportantExcerpt<'a> {part: &'a str,
}impl<'a> ImportantExcerpt<'a> {fn level(&self) -> i32 {3}
}
  • 为ImportantExcerpt实现了方法level;
  • 方法level唯一的参数是self引用;
  • impl和类型名称之后的生命周期参数是必须要有的;
  • 不必须标注 self 引用的生命周期;

适用于输出生命周期第二条规则的例子

impl<'a> ImportantExcerpt<'a> {fn announce_and_return_part(&self, announcement: &str) -> &str {println!("Attention please: {}", announcement);self.part}
}
  • 有两个输入生命周期,Rust应用输入生命周期的规则给予 &self 和 announcement 他们各自的生命周期;
  • 由于其中一个参数是&self,返回值类型被赋予了&self 的生命周期;

6.8 静态生命周期

  • 'static的生命周期能够存活于整个程序期间;
  • 所有的字符串字面量都拥有'static生命周期;
  • 也可以显式标注;
  • 字符串的文本被直接储存在程序的二进制文件中而这个文件总是可用的;
let s: &'static str = "I have a static lifetime.";

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

use std::fmt::Display;fn longest_with_an_announcement<'a, T>(x: &'a str, y: &'a str, ann: T) -> &'a strwhere T: Display
{println!("Announcement! {}", ann);if x.len() > y.len() {x} else {y}
}
  • 上面演示了在同一函数中指定泛型类型参数、trait bounds 和生命周期的语法;
  • 示例返回两个字符串 slice 中较长者的函数且带有一个额外的参数 ann;
  • ann 的类型是泛型 T,它可以被放入任何实现了 where 从句中指定的 Display trait 的类型;
  • 这个额外的参数会在函数比较字符串 slice 的长度之前被打印出来(因此Display trait bound是必须的);
  • 生命周期也是泛型,所以生命周期参数 'a 和泛型类型参数 T 都位于函数名后的同一尖括号列表中;

这篇关于13.泛型、trait和生命周期(下)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1067211

相关文章

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

Maven(插件配置和生命周期的绑定)

1.这篇文章很好,介绍的maven插件的。 2.maven的source插件为例,可以把源代码打成包。 Goals Overview就可以查看该插件下面所有的目标。 这里我们要使用的是source:jar-no-fork。 3.查看source插件的example,然后配置到riil-collect.xml中。  <build>   <plugins>    <pl

Java泛型类型解析

解析泛型类型 获取字段泛型类型 **java.lang.reflect.Field#getGenericType**: 作用:返回字段的泛型类型。返回类型:Type。如果字段是一个泛型类型,这个方法将返回一个表示这个泛型类型的 Type 对象,比如 ParameterizedType,TypeVariable 等等。如果字段不是泛型类型,这个方法将返回字段的具体类型,即 Class 对象。示例

【Vue】关于Vue3的生命周期

目录 Vue3中新增了一个setup生命周期函数:(1) setup执行的时机是在beforeCreate生命周期函数之前执行,在setup函数中是不能通过this来获取实例的;(2) 为了命名的统一性,将beforeDestroy 改名为 beforeUnmount,destroyed 改名为 unmounted 生命周期函数: setup —— 不能通过this来获

13 transition数组的动画使用

划重点 动画:transitiontransition-group :数组动画数组的 添加 / 删除 豆腐粉丝汤 清淡又健康 <!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><me

09 生命周期

生命周期 beforeCreatecreatedbeforeMountmountedbeforeUpdateupdatedbeforeDestorydestoryed 辣子鸡:香辣入口,犹如吃了炫迈一样 - - - 根本停不下来 <!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport"

【CTF Web】BUUCTF Upload-Labs-Linux Pass-13 Writeup(文件上传+PHP+文件包含漏洞+PNG图片马)

Upload-Labs-Linux 1 点击部署靶机。 简介 upload-labs是一个使用php语言编写的,专门收集渗透测试和CTF中遇到的各种上传漏洞的靶场。旨在帮助大家对上传漏洞有一个全面的了解。目前一共20关,每一关都包含着不同上传方式。 注意 1.每一关没有固定的通关方法,大家不要自限思维! 2.本项目提供的writeup只是起一个参考作用,希望大家可以分享出自己的通关思路

Chapter 13 普通组件的注册使用

欢迎大家订阅【Vue2+Vue3】入门到实践 专栏,开启你的 Vue 学习之旅! 文章目录 前言一、组件创建二、局部注册三、全局注册 前言 在 Vue.js 中,组件是构建应用程序的基本单元。本章详细讲解了注册和使用 Vue 的普通组件的两种方式:局部注册和全局注册。 本篇文章参考黑马程序员 一、组件创建 ①定义 Vue 组件是一种具有特定功能的 Vue 实

Maven生命周期:深入理解构建过程

目录 1. Maven生命周期简介 2. 默认生命周期的阶段 3. 清理生命周期 4. 站点生命周期 5. Maven生命周期的灵活性 6. 结论         在Java开发中,Maven是一个不可或缺的工具,它通过自动化项目的构建、依赖管理和文档生成等任务,极大地提高了开发效率。Maven的核心之一是其构建生命周期,它定义了项目构建过程中的一系列阶段。在这篇文章中,我们将深

VMware Fusion Pro 13 Mac版虚拟机 安装Win11系统教程

Mac分享吧 文章目录 Win11安装完成,软件打开效果一、VMware安装Windows11虚拟机1️⃣:准备镜像2️⃣:创建虚拟机3️⃣:虚拟机设置4️⃣:安装虚拟机5️⃣:解决连不上网问题 安装完成!!! Win11安装完成,软件打开效果 一、VMware安装Windows11虚拟机 首先确保自己的mac开启了网络共享。不然虚拟机连不上👀的 1️⃣:准备镜像