Rust编程(三)生命周期与异常处理

news2024/10/5 14:44:24

生命周期

在这里插入图片描述

生命周期,简而言之就是引用的有效作用域。在大多数时候,我们无需手动的声明生命周期,因为编译器可以自动进行推导。生命周期的主要作用是避免悬垂引用,它会导致程序引用了本不该引用的数据:

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

这种情况r就是悬垂指针,r是引用的x,但是x的生命周期是到大括号结束就结束了,所以下面再使用r时,r就是一个悬垂指针。避免悬垂指针的方法就是:确保被引用的变量的生命周期长于引用本身。

fn main() {
    let string1 = String::from("abcd");
    let string2 = "xyz";

    let result = longest(string1.as_str(), string2);
    //会报错,因为编译器不知道返回是x还是y,自然就没法对result进行生命周期分析
    println!("The longest string is {}", result);
}

fn longest(x: &str, y: &str) -> &str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
} 

对于一些复杂情况,编译器无法自己推导出生命周期,就需要我们自己进行生命周期标注:

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

生命周期标注不对代码的逻辑起到任何作用,它自身并不具有什么意义,因为生命周期的作用就是告诉编译器多个引用之间的关系。

标注后的longest函数:

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    //这里告诉编译器,x和y的生命周期要长于`a
    //然后将二者的生命周期统一设定为较短的那个的生命周期
    //这样就不管是返回x还是y,result的生命周期都是一样的
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

当只与一个参数有关时,可以只标注一个的生命周期:

fn function<'a>(arg1:&'a mut String,arg2:& mut String) -> &'a mut String{
    return arg1;
}

fn main(){
    let mut s1 : String = String::new();
    let mut s2 : String = String::new();
    let s3 = function(&mut s1,&mut s2);
    println!("s3:{}",s3);
}

生命周期问题只针对于引用,主要是为了解决引用/借用还在,被引用/借用的没了的情况,对于move就没有这么麻烦,一旦move,直接生命周期就重新计算了,与之前的变量就没关系了。

结构体也有生命周期的要求,结构体内部有引用,被引用的对象的生命周期要长于结构体对象的生命周期:

struct ImportantExcerpt<'a> {
    part: &'a str,
}

fn main() {
    let novel1 = String::from("Call me Ishmael. Some years ago...");
    let first_sentence1 = novel1.split('.').next().expect("Could not find a '.'");
    let i = ImportantExcerpt {
        part: first_sentence1,
    }; //这种没有问题
    
    let i;
    {
        let novel2 = String::from("Call me Ishmael. Some years ago...");
        let first_sentence2 = novel2.split('.').next().expect("Could not find a '.'");
        i = ImportantExcerpt {
            part: first_sentence2,
        };
    }
    println!("{:?}",i); //这种就会报错,因为first_sentence2在大括号结束的时候生命周期已经结束了
}

函数情况下分为输入生命周期和输出生命周期,一些编译器可以推断出输入输出生命周期的情况,就不需要进行生命周期标注,编译器推断生命周期有三个规则:

  • 每一个引用参数都会获得独自的生命周期
    例如一个引用参数的函数就有一个生命周期标注: fn foo<'a>(x: &'a i32),两个引用参数的有两个生命周期标注:fn foo<'a, 'b>(x: &'a i32, y: &'b i32), 依此类推。

  • 若只有一个输入生命周期(函数参数中只有一个引用类型),那么该生命周期会被赋给所有的输出生命周期,也就是所有返回值的生命周期都等于该输入生命周期

  • 例如函数 fn foo(x: &i32) -> &i32,x 参数的生命周期会被自动赋给返回值 &i32,因此该函数等同于 fn foo<'a>(x: &'a i32) -> &'a i32

  • 若存在多个输入生命周期,且其中一个是 &self 或 &mut self,则 &self 的生命周期被赋给所有的输出生命周期。拥有 &self 形式的参数,说明该函数是一个 方法,该规则让方法的使用便利度大幅提升。
    更多关于生命周期的内容,请参考

函数返回/异常处理

在这里插入图片描述

Rust的函数返回为了解决是否有值的问题,引入了一个枚举类型Option,有值时返回Some(T),没值时返回None,再也不用绞尽脑汁如何思考返回-1或者None了。

enum Option<T> {
    Some(T),
    None,
}

Rust的异常分为可恢复异常Result,出了异常不直接崩溃,还可以根据异常的类别继续运行,类似于其他语言中函数返回了-1或者None。另一种是不可恢复异常panic,出了问题直接崩溃,类似于其他语言中的assert。

Result<T,E>

对应可以挽救的error,Result是一个枚举类型

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

使用案例:

fn plus_one(x: Option<i32>) -> Option<i32> {
    match x { //match就类似于C里面的switch语句
        None => None,
        Some(i) => Some(i + 1),
    }
}

let five = Some(5);
let six = plus_one(five);
let none = plus_one(None);

当函数正常运行时,返回Ok包裹着的返回结果,当函数运行失败时,返回Err包裹的错误类型,
一般使用Result的工作流程就是:函数返回一个Result,然后使用match匹配结果:

use std::fs::File;
use std::io::ErrorKind;

fn main() {
    let f = File::open("hello.txt");

    let f = match f {
        Ok(file) => file,
        Err(error) => match error.kind() {
            ErrorKind::NotFound => match File::create("hello.txt") {
                Ok(fc) => fc,
                Err(e) => panic!("Problem creating the file: {:?}", e),
            },
            other_error => panic!("Problem opening the file: {:?}", other_error),
        },
    };
}

Result也可以通过unwrap或者except转换为panic:

use std::fs::File;

fn main() {
    let f = File::open("hello.txt").unwrap();
    //不出问题就提取出来返回结果,出问题就直接panic
    let f = File::open("hello.txt").expect("Failed to open hello.txt");
    //跟上面一样的效果,不过会报错自定义的错误提示
}

panic

对应不可恢复error,出现panic代码直接崩溃

fn main() {
    panic!("crash and burn");
}

更多关于panic的请看Rust圣经

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

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

相关文章

Autosar-CanNm、Nm配置详解(免费)-1

3.1由DBC创建Nm、CanNM ETAS工具可根据DBC文件&#xff0c;自动配置生成Nm、CanNm模块。但是关键的一点是要生成NM、CanNM模块DBC文件中必须有NM类型的报文。 还有一点&#xff0c;即使DBC文件中有Nm的报文&#xff0c;但是因为报文的类型在导入时没有设置成Nm&#xff0c;那也…

Springboot整合Redis报错:Unable to connection Redis

今天在做Springboot整合Redis中碰到下列错误&#xff1a; 基于以上的错误首先在Xshell或者其他远程操控虚拟机的软件上看能不能连接到Redis: [zzllocalhost ~]$ redis-cli -h 192.168.136.132 -p 6379 -a ****** Warning: Using a password with -a or -u option on the comma…

kubectl 启用shell自动补全功能

官网手册参考&#xff1a;https://kubernetes.io/zh-cn/docs/tasks/tools/install-kubectl-linux/ 系统&#xff1a;centos7 补全脚本依赖于工具 bash-completion&#xff0c; 所以要先安装它&#xff08;可以用命令 type _init_completion 检查 bash-completion 是否已安装&a…

如何解决了“该虚拟机似乎正在使用中”问题

一、问题描述 1、在用VMware虚拟机的时候&#xff0c;有时会发现打开虚拟机时提示“该虚拟机似乎正在使用中。如果该虚拟机未在使用&#xff0c;请按“获取所有权(T)”按钮获取它的所有权。否则&#xff0c;请按“取消©”按钮以防损坏。配置文件: D:\win10x64\Windows 10…

李宏毅深度强化学习导论——策略梯度

引言 这是李宏毅老师深度强化学习视频的学习笔记&#xff0c;主要介绍策略梯度的概念&#xff0c;在上篇文章的末尾从交叉熵开始引入策略梯度。 如何控制你的智能体 上篇文章末尾我们提到了两个问题&#xff1a; 如何定义这些分数 A A A&#xff0c;即定义奖励机制&#xff…

栈和队列.

目录 1.栈 1.1栈的概念及结构 1.2栈的实现 2.队列 2.1队列的概念及结构 2.2 队列的实现 1.栈 1.1栈的概念及结构 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端 称为栈顶&#xff0c;另一端称为栈底…

【数据库管理操作】Mysql 创建学生数据库及对数据表进行修改

MySQL 创建学生成绩数据库 1.创建数据库 create database studentscore;创建完成之后&#xff0c;如果需要使用该数据&#xff0c;使用use命令 use studentscore;创建表前查看当前数据库中包含的表 show tables; 2.创建bclass表 create table bclass( class_id char(8) …

python 客户管理系统 增删改查 以及 用户带权限的功能。

# readme1、csss.py文件 &#xff0c;他是一个初始化程序&#xff0c;只运行一次即可 2、有一个目录是data,他是保存客户信息和用户信息 3、customer.py保存了一个客户类 4、user.py保存了一个用户类 5、main.py是客户信息管理程序&#xff0c;可以添加客户信息&#xff0c;删除…

7.JDK下载和安装

文章目录 一、下载二、安装三、JDK的安装目录介绍 写JAVA代码不是随随便便能写的&#xff0c;我们得先做一点准备工作。例如&#xff0c;我们平时想要玩一把游戏&#xff0c;就需要先下载、安装才能玩游戏。JAVA也是一样的&#xff0c;也是需要下载并安装相关的软件&#xff0c…

win10休眠功能误删了

背景&#xff1a; 今天用某电脑管家清理C盘&#xff0c;不小心把休眠文件给删了&#xff0c;结果电脑失去了休眠功能。这玩意对我太重要了&#xff0c;我合上盖子就是要电脑休眠带走的。于是开始找办法&#xff0c;终于在官网找到了。并且恢复成功。 话不多说直接上技能&#…

浏览器如何渲染页面

浏览器渲染页面的过程是一个多步骤的复杂流程&#xff0c;下面我将通过一个简单的例子来逐步说明这一过程&#xff1a; 以下是浏览器渲染该页面的主要步骤&#xff1a; 请求HTML文档&#xff1a; 用户在浏览器中输入URL并回车&#xff0c;浏览器向服务器发送HTTP请求。 服务器…

Spring面试题整理(持续更新)

Spring框架中的单例Bean是线程安全的吗&#xff1f; Spring中的Bean默认是单例模式的&#xff0c;框架并没有对bean进行多线程的封装处理。如果Bean是有状态的 那就需要开发人员自己来进行线程安全的保证&#xff0c;最简单的办法就是改变bean的作用域 把 "singleton"…

【YOLOv5改进系列(5)】高效涨点----添加密集小目标检测NWD方法

文章目录 &#x1f680;&#x1f680;&#x1f680;前言一、1️⃣ 修改loss.py文件1.1 &#x1f393; 修改11.2 ✨ 修改21.3 ⭐️相关代码的解释 二、2️⃣NWD实验2.1 &#x1f393; 实验一&#xff1a;基准模型2.2 ✨实验二&#xff1a;NWD权重设置0.52.3 ⭐️实验三&#xf…

安捷伦Agilent E5071B网络分析仪

181/2461/8938产品概述&#xff1a; Agilent E5071B 网络分析仪可为射频组件提供快速、准确的测量。与同类网络分析仪相比&#xff0c;其宽动态范围和低迹线噪声可实现更高的测试质量和吞吐量。内置 2、3 和 4 个测试端口可同时测量具有最多四个端口的组件的所有信号路径。Agi…

大促销活动时期如何做好DDoS防护?

每一次活动大促带来的迅猛流量&#xff0c;对技术人而言都是一次严峻考验。如果在活动期间遭受黑产恶意DDoS攻击&#xff0c;无疑是雪上加霜。电商的特性是业务常态下通常不会遭受大流量DDoS攻击&#xff0c;且对延迟敏感&#xff0c;因此只需要在活动期间按需使用DDoS防护。本…

设置asp.net core WebApi函数输入和返回类型中的属性名称开头大小写格式

以下列类型定义为例创建简单的ASP.NET Core的WebApi函数&#xff0c;此时输入参数和返回结果的属性名称开头默认为小写&#xff0c;如下图所示。 public class UserInfo { public string UserName { get; set; }public string UserSex { get; set; }public string UserP…

腾讯云免费云服务器申请流程详解

随着云计算的普及&#xff0c;越来越多的企业和个人开始选择使用云服务器。腾讯云作为国内领先的云计算服务提供商&#xff0c;为用户提供了丰富的云产品和服务。本文将为大家详细介绍腾讯云免费云服务器的申请流程。 一、注册腾讯云账号 首先&#xff0c;需要注册一个腾讯云账…

Qt 压缩/解压文件

前面讲了很多Qt的文件操作&#xff0c;文件操作自然就包括压缩与解压缩文件了&#xff0c;正好最近项目里要用到压缩以及解压缩文件&#xff0c;所以就研究了一下Qt如何压缩与解压缩文件。 QZipReader/QZipWriter QZipReader 和 QZipWriter 类提供了用于读取和写入 ZIP 格式文…

Healix Protocol 的 HLX 通证预售:医疗领域的未来展望

Healix Protocol推出 HLX 通证预售&#xff0c;将带来医疗领域的重要变革。通过其区块链技术&#xff0c;Healix Protocol致力于重新定义医疗服务的可及性与负担性&#xff0c;成为医疗行业的希望之光。该项目旨在增强透明度、可及性和效率&#xff0c;推动医疗体系向更加公平和…

Shut down, sleep, or hibernate your PC 关闭、睡眠或休眠

最近一段时间没有整服务器了~自己开始捉摸18年买的笔记本-x280&#xff0c;除了发现usb type c和thunderbolt 3接口的不一样外&#xff0c;也开始研究这个待机的功能了~找了官方文档&#xff0c;做个简易的翻译&#xff0c;给大家一起看看学习把。 关于Windows的 “睡眠” 和 …