Rust学习笔记(9)——Option的几个方法及所有权问题

rust学习五、认识所有权 按照rust官方的说法,rust要保证内存安全(一定会释放掉),同时还需要保证高效(不能用java 那样的gc管理器),同时还不能太繁琐(像C++那样手动释放)注:第二条规则“任意时刻,一个值只能有一个所有者”这个似乎有问题,因为在书本的第十五章节的Rc(引用计数)指针又声称"然而,有些情况单个值可能会有多个所有者"通过这个提示我们可以看到:在一个作用域之内,如果一个变量转赋给另外一个变量的时候,会发生一件事情:值会从一个变量转到另外一个变量上。一个变量的值被移走后,那么再次使用原来的变量就变得麻烦了。 阅读详情
AI摘要

本文探讨Rust中Option的巧妙设计如何避免null问题,尤其在所有权和引用约束下处理链表、图等数据结构时的复杂性。重点讲解unwrap、take方法的使用,以及as_ref和as_mut在借用和数据修改中的作用。

在rust的设计中,Option的设计非常巧妙,避免了其它语言中常见的Null引起的各种错误和异常。但Option与所有权的结合,尤其是在一些特定的数据结构,如链表、图等设计中,加上引用的各种约束,就使得Option的使用变得复杂起来。

1、unwrap方法

在确认Option不为None的情况下,可以用unwrap方法拆解出其中的值,并获取值的所有权。这里要强调的是,unwrap会消费Option本身的值,后面就不能再用了。

    let a = Some(Box::new(5));
    let d = a.unwrap();
    // println!("{:?}", a); // cannot use 'a' again.
    println!("{:?}", d);

unwrap开头的还有一系列方法,分别对应各种情况,可以参考官方标准库的API文档。

但这里有一个所有权的限制,因为涉及到其内部值的所有权的转移,所以只有Option原始变量本身可以调用unwrap方法,其引用(包括可变引用)均不可调用。这和unwrap的实现方法有关系,因为其消费了原始变量。下方代码不可编译:

    let mut a = Some(Box::new(5));
    let b = &mut a;
    let c = b.unwrap(); // Error! cannot move out of `*b` which is behind a mutable reference

这种设计让链表的实现变得困难,结合前面学习到的struct的引用情况下的所有权的限制,形成了一个所有权引用借用的“迷宫”。

unwrap的实现源代码,供参考学习:

    #[inline]
    #[track_caller]
    #[stable(feature = "rust1", since = "1.0.0")]
    #[rustc_const_unstable(feature = "const_option", issue = "67441")]
    pub const fn unwrap(self) -> T {
        match self {
            Some(val) => val,
            None => panic!("called `Option::unwrap()` on a `None` value"),
        }
    }

2、take方法

take方法可以从Option中取出值,为原来的Option变量留下None值,但原来的变量还有效(但实际上没什么用处了,因为只剩下None了)。take方法的原始值或者引用必须为mut类型。强调一下,借用情况下可以调用take方法。或者说指向Option的可变引用(指针)可以调用take方法。

fn main() {
    let mut a = Some(Box::new(5));
    let mut b = &mut a;
    let c = &mut b;
    let d = c.take();
    println!("{:?}", c);
    println!("{:?}", d);
    println!("{:?}", a);
}

输出:

None
Some(5)
None

但上面这两个方法,都改变了Option变量本身。如果不改变或受到限制无法改变Option本身时,只想借用或可变借用其中wrap的值应该怎么办呢?这也是在实现链表时遇到的让人掉坑的问题。幸好Option提供了 as_ref 和 as_mut方法。

3、as_ref 方法

as_ref方法的签名是:

fn as_ref(&self) -> Option<&T>

该方法将对Option的引用变为对Option所包含对象的不可变引用,并且返回一个新的Option。对这个新的Option进行unwrap操作,可以获得原Option所包含的对象的不可变引用。原始Option变量及其引用,均可调用as_ref方法。例如:

    let a = Some(Box::new(5));
    let b = a.as_ref();
    let c = b.unwrap();

    println!("{:?}", a); 
    println!("{:?}", b);
    println!("{:?}", c);

输出为:

Some(5)
Some(5)
5

强调几点:

1、在以上代码中没有改变原始变量a,故a仍然可用;

2、b为通过as_ref生成的一个新的Option;对b进行unwrap,不影响a;

3、c为a/b包含的Box的不可变引用。

疑惑的一点:在实验过程中,不像在第一点中提到的unwrap方法,对变量b进行unwrap后,b仍然可用,这点和在第一点中遇到的不一样,不知道为什么。可能还是对rust学习的不够深入吧。

下面稍微复杂一点的代码也可以运行,但也有疑惑的一点:e为c的引用,按照第一点学的,引用不能调用unwrap方法,但这里竟然也可以调用,也和之前学的不一致,需要以后对rust更熟悉了再来看。

fn main() {
    let a = Some(Point { x: 5, y: 5 });
    let b = &a;
    let c = b.as_ref(); // c is a new Option;
    let e = &c;
    let d = e.unwrap();

    println!("{:?}", d.x);
    println!("{:?}", c);
    println!("{:?}", e); // ?
    println!("{:?}", b);
    println!("{:?}", a);
}

#[derive(Debug)]
struct Point {
    x: i32,
    y: i32,
}

4、as_mut方法

与as_ref类似,as_mut只是返回了Option包含的数据的可变引用,但在实际实验时,发现其和as_ref还是有一些细微的区别,也让人有些不明白,如下方代码:

    let mut a = Some(Point { x: 5, y: 5 });
    let b = &mut a;
    let c = b.as_mut(); // c is a new Option;
    let e = &c;
    let d = e.unwrap(); // Error! cannot move out of `*e` which is behind a shared reference

e为c的引用,最后一行又报错了,又不允许通过引用调用unwrap方法了,又和第一点学的保持一致了!同时,对于新生成的Option值,在调用unwrap方法后,也变得不再可用了。下面代码可以编译运行(Point与上面相同):

fn main() {
    let mut a = Some(Point { x: 5, y: 5 });
    // let b = a.as_mut();
    let b = &mut a;
    let c = b.as_mut(); // c is a new Option;
    let d = c.unwrap();

    d.x += 10;

    let e = &mut d.y;
    *e += 20;

    println!("{:?}", d.x);
    // println!("{:?}", c); // c is not available because of method call of "unwrap".
    println!("{:?}", b);
    println!("{:?}", a);
}

输出:

15
Some(Point { x: 15, y: 25 })
Some(Point { x: 15, y: 25 })

通过以上代码可以看出,在未改变Option变量a的情况下,通过as_mut方法,改变了其包含的数据的值。这个能力对于编写链表时,尤其是节点的插入、删除时,可以灵活的操作指向下一个节点的指针。

说明一点,调用as_mut方法时,Option变量本身或其引用,必须为可变的,即mut类型。

简单类型的一个例子:

fn main() {
    let mut a = Some(5);
    let b = a.as_mut().unwrap();
    *b += 10;

    println!("b = {:?}", b);
    println!("a = {:?}", a);
}

输出:

b = 15
a = Some(15)

 

Rust 认识所有权 常见的编程语言管理其运行时使用计算机内存的方式一般是垃圾回收机制、程序员亲自分配和释放内存。Rust 则选择了第三种方式:通过所有权系统管理内存,编译器在编译时会根据一系列的规则进行检查。如果违反了任何这些规则,程序都不能编译。 阅读详情

相关推荐

深入理解 Rust 所有权

Rust的所有权机制是其内存管理的核心创新,通过编译期严格检查确保内存安全。本文从底层堆栈知识入手,解释了所有权产生的背景:当多个变量指向同一堆内存时,传统语言面临释放时机的难题。Rust的解决方案是确立"唯一所有者"原则,当所有权转移时原变量失效,并在所有者离开作用域时自动调用drop释放资源。该机制依赖三大语言特性:自动析构机制保证资源释放,编译期检查确保所有权转移合规,以及Copy trait区分复制语义。所有权转移不仅发生在赋值时,也存在于函数参数传递和返回值场景。这种设计既避免

Laurence的技术博客 2344

Rust——关于Option详解

Option是组成Rust程序的基石,熟练使用Rust的Option可以帮助我们进行程序的开发。但是Option这里的知识和细节比较绕,说白了就是各种套娃,本篇文章意在梳理Option的一些细节。

JAN6055的博客 8703

Python、Selenium实现问卷星自动填写(内含适配个人问卷的方法)

使用Python、Selenium实现问卷星自动填写,内含适配个人问卷的方法。

DaenCode的博客 3837

【Rust 基础篇】Rust Option 类型详解

Option 枚举有两个变体:Some 和 None。Some 变体包含一个值,表示存在某个值;None 变体表示没有值。Option 类型使用泛型参数 T,表示可能存在的值的类型。通过泛型,我们可以在 Option 类型中存储任意类型的值。Option 类型是 Rust 中一种重要的工具,用于处理可能为空的值。通过使用 Option 类型,我们可以在编译时发现并处理潜在的空值错误,编写出更安全和可靠的代码。

繁依Fanyi的博客 2569

windows系统下,如何为comfyui如何安装triton

​ 有些加速插件、模型在安装的时候,比如说sageattention的安装,就需要提前安装triton这个包,不过这个包有点特殊,windows下略微不好装,经常会装失败。 🎠如何安装 之前也有人介绍过,要到github上找个wheel文件,找到对应的版本安装下就好了。 问题是:https://github.com/woct0rdho/triton-windows/releases 这个git地址上,已经找不到wheel文件了... ​

聚梦小课堂 4499

LABVIEW学习之MODBUS-TCP通讯

LABVIEW学习之MODBUS-TCP通讯 1,MODBUS-TCP通讯介绍: MODBUS通讯是工业上面经常用到的一种通讯协议。该协议的原理及介绍不在此累述,下面直接介绍LABVIEW对此协议的应用。 2,MODBUS通讯所需的函数: 安装了DSC模块之后可以在数据通信内找到相关函数 3 ,主VI 主要思路就是使用状态机来进行连接 读取 写入 断开。使用枚举类型来选择状态: 1,连接 2,写入多个M继电器: 3,写入单个M继电器 4,写入双字节D...

风动9527的博客 2万+

Rust 函数

使用fn关键字定义函数,函数由函数签名和函数体组成。函数签名由函数名、参数、返回值类型组成函数体包含于{}内,是函数要执行的具体代码。函数需要调用才会执行。x + y // 结尾没有分号 } fn main() {let y = {x + 1 // 结尾没有分号 };println!}main函数main函数是程序的入口函数,对于可执行文件来说,main函数是必不可少的,对于库函数来说,main函数不是必须的函数体函数体由一系列语句和一个可选的结尾表达式构成。

wkk的博客 1818

Abaqus 2023 保姆级安装避坑指南:从环境变量到许可证配置,一次搞定

本文提供Abaqus 2023的详细安装指南,涵盖环境变量设置、许可证配置等关键步骤,帮助用户避开常见安装陷阱。通过系统环境检查、目录结构规划、许可证服务器安装及Java环境配置等实用技巧,确保顺利完成安装并优化软件性能。

weixin_42524276的博客 426

Rust编程知识拾遗:Rust 编程,Option 学习

视频地址 头条地址:https://www.ixigua.com/i6765442674582356483 B站地址:https://www.bilibili.com/video/av78062009?p=1 网易云课堂地址:https://study.163.com/course/introduction.htm?courseId=1209596906#/courseDetail?tab=1 g...

令狐一冲 2528

heygem本地部署报错:SQLite3 can only bind numbers, strings, bigints, buffers, and null...如何解决?

🏆本文收录于 《全栈Bug调优(实战版)》 专栏,该专栏专注于分享我在真实项目开发中遇到的各类疑难Bug及其深层成因,并系统提供高效、可复现的解决思路和实操方案。无论你是刚入行的新手开发者,还是拥有多年项目经验的资深工程师,本专栏都将为你提供一条系统化、高质量的问题排查与优化路径,助力你加速成长,攻克技术壁垒,迈向技术价值最大化与职业发展的更高峰🚀!    📌 特别说明: 文中部分技术问题来源于真实生产环境及网络公开案例,均经过精挑细选与系统化整理,并结合多位大厂资深架构师和一线开发多年实战经验沉淀,

**My Coding Family** 3246

智能车图像处理11-进阶篇3--中线累积差值的统计

中线累积差值的统计在后面判断小S,以及长直道的时候都有用到,关键点在于中线的跳变 第12 - 13 行:comulants1 和comlants2代表的是两种不同的累差,取的行数不同 第14-17 行:comulants2取6到25行,其实就是My_Abs(LCenter[i], LCenter[i - 1])的累加 第18-21 行:comulants1去第1到40行 第27- 行: 计算了一下右线的开始于最终结束行,这里判断R_black是否为0,是和中线的提取中g

taiyuezyh的博客 3827

Rust从入门到暴走(18)--- Rust的Option使用详解

Option 类型是一个枚举类型,它有两个变体:Some(T):表示存在一个值,这个值被包含在 Some 中。None:表示不存在值。

Luca的博客, 网络安全Hacker,人工智能AIer,区块链Web3er,全栈软件开发er 2045

【BlueDroid】【PBAP】蓝牙电话本协议之同步通讯录分析(超详细)

Header - Type = "x-bt/vcard-listing":代表了该Request对应的Function为PullvCardListing;Profile:PBAP (4),上层应用层协议为PBAP,OBEX协议的上层应用层协议除了PBAP,还有OPP、MAP协议;Response in Frame:该request frame-485对应的Response frame为489;在PBAP协议同步通讯录和通讯记录中,都是基于OBEX协议实现的,PBAP协议作为了应用层协议;

风云说通信 2073

Rust 学习笔记:所有权

Rust 学习笔记:所有权

ProgramNovice的博客 2208

用MATLAB计算卡方值

然后,我们计算了期望频数,即在两个分类变量独立的情况下,每个单元格的预期频数。接下来,我们使用卡方检验的公式计算了卡方值。卡方检验是一种常用的统计方法,用于确定两个分类变量之间是否存在相关性。它基于观察到的频数和期望频数之间的差异来评估分类变量之间的独立性。在MATLAB中,我们可以使用。请注意,这只是一个简单的示例代码,用于说明如何使用MATLAB计算卡方值。在实际应用中,您可能需要根据您的具体数据和研究问题进行相应的修改和调整。在上面的代码中,我们首先创建了两个分类变量。函数显示了计算得到的卡方值。

CodeNexus的博客 2658

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

【Rust学习笔记】Rust 的所有权介绍

在 Rust 中,所有权这个概念内置于语言本身,并通过编译期检查强制执行。每个值都有决定其生命周期的唯一拥有者。当拥有者被释放时,它拥有的值也会同时被释放,在 Rust 术语中,释放的行为被称为丢弃(drop)。这些规则便于通过检查代码确定任意值的生命周期,也提供了系统级语言本应支持的对生命周期的控制。

从善若水的博客 1228

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

【Rust】所有权

所有权是一套规则,用于管理Rust程序如何使用内存。Rust通过所有权系统来管理内存,编译器会检查这些规则的遵守情况。违反任何规则将导致程序无法编译。所有权的特点是不会影响程序的运行速度。

qq_61867911的博客 1587

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

Rust中的所有权是什么

既然我们已经掌握了基本语法,将不会在之后的例子中包含 fn main() { 代码,所以如果你是一路跟过来的,必须手动将之后例子的代码放入一个 main 函数中。这样,例子将显得更加简明,使我们可以关注实际细节而不是样板代码。 在所有权的第一个例子中,我们看看一些变量的 作用域(scope)。作用域是一个项(item)在程序中有效的范围。假设有这样一个变量: 变量 s 绑定到了一个字符串字面值,这个字符串值是硬编码进程序代码中的。这个变量从声明的点开始直到当前 作用域 结束时都是有效的。示例中的注释标明了变

write less , do more 2432

Rust所有权学习笔记

本文是的学习笔记。大部分代码是菜鸟教程中给出的,文字稍加提炼,加入了一些自己的理解我们都知道,C/C++需要申请和释放内存,即手动管理内存。所有权概念是为了让 Rust 在编译阶段更有效地分析内存资源的有用性以实现内存管理而诞生的概念。所有权是Rust语言的精髓之一!

qq_74143285的博客 795

Rust 所有权机制

所有权ownership)是 Rust 用于如何管理内存的一组规则。所有程序都必须管理其运行时使用计算机内存的方式。一些语言中具有垃圾回收机制,在程序运行时有规律地寻找不再使用的内存;在另一些语言中,程序员必须亲自分配和释放内存。Rust 则选择了第三种方式:通过所有权系统管理内存,编译器在编译时会根据一系列的规则进行检查。如果违反了任何这些规则,程序都不能编译。在运行时,所有权系统的任何功能都不会减慢程序。

分享Java,Rust相关技术 1102

Rust学习笔记004:Rust的所有权机制

移动构造函数的目标是在没有不必要的资源复制的情况下,将资源从一个对象“移动”到另一个对象,以提高效率。在C++中,移动构造函数(Move Constructor)是一种特殊的构造函数,用于在对象的所有权被转移(移动)时创建新对象。移动构造函数通常用于提高性能,特别是在涉及资源管理的情况下,比如使用动态内存分配的对象。这意味着在函数中你可以使用该值,但在函数返回后,调用者将无法再使用它。在 Rust 中,所有权系统与函数的参数传递和返回密切相关,它确保在函数调用过程中对所有权的正确管理。类具有移动构造函数。

ResumeProject的博客 1919

Rust 所有权规则

rust 所有权规则、引用、借用、slice 切片、字符串字面量

qq_33222522的博客 1434

Rust学习笔记-认识所有权

出于同样原因,处理器在处理的数据彼此较近的时候(比如在栈上)比较远的时候(比如可能在堆上)能更好的工作。不幸的是,我们不能为了每一个在编译时大小未知的文本而将一块内存放入二进制文件中,并且它的大小还可能随着程序运行而改变。在大部分没有 GC 的语言中,识别出不再使用的内存并调用代码显式释放就是我们的责任了,跟请求内存的时候一样。图 4-3 中的那样,如果 Rust 也拷贝了堆上的数据,那么内存看起来就是这样的。类型,为了支持一个可变,可增长的文本片段,需要在堆上分配一块在编译时未知大小的内存来存放内容。

快乐的小小程序猿 1277

Rust 所有权机制详解

通过编译时检查确保内存安全,核心规则包括每个值有唯一所有者、移动语义和显式克隆。主要特性:1)所有权转移发生在变量赋值、函数传参和返回值时;2)借用机制允许不可变/可变引用;3)生命周期注解解决引用失效问题。优势包括零成本抽象、线程安全和明确资源管理。示例代码展示了资源创建、所有权转移、借用规则及集合中的所有权管理,体现了Rust在保证高性能同时实现内存安全的独特设计。

本博客分享软硬件一体化开发实战。硬件涵盖 PCB 设计、STM32 等 MCU 底层开发;软件包含 C++、Rust,Qt/QML、PyQt、Slint、TouchGFX、LVGL 各类 GUI 框架。方便软硬件开发者查阅学习。欢迎关注! 2063

【Rust】所有权的认识

所有程序都必须管理其运行时使用计算机内存的方式。一些语言中具有垃圾回收机制,在程序运行时有规律地寻找不再使用的内存;在另一些语言中,程序员必须亲自分配和释放内存。Rust 则选择了第三种方式:通过所有权系统管理内存,编译器在编译时会根据一系列的规则进行检查。如果违反了任何这些规则,程序都不能编译。在运行时,所有权系统的任何功能都不会减慢程序。Rust 中的每一个值都有一个 所有者(owner)。值在任一时刻有且只有一个所有者。当所有者(变量)离开作用域,这个值将被丢弃。

zdsey的博客 1474

rust 的所有权

假设是 go 语言来实现同样的代码,程序没有任何问题,s1 和 s2 实际指向的同样的字符串,可以正常打印 s1,赋值前后压根不会对 s1 产生影响。快速解决上述问题的方案,是将赋值的方式修改为引用赋值,就像下面这样,给 s2 赋值的是 s1 的引用,这样 s2 就不能接管 neojos 字符串的所有权,引用赋值过程就属于。比如下面的例子,s2 指向了 s1 的可修改引用,s3 试图通过从 s2 取值,那么,s1 变量所拥有的值会发生转移吗?由 s1 转移给了 s2,然后 s1 变成了未初始化的状态。

小知识折射大智慧 1266
上一篇: Rust学习笔记(8)——连续引用的约束和限制
下一篇: Rust学习笔记(10)——引用和解引用的所有权问题
Codevalley
博客等级 码龄9年 22粉丝 27原创
评论 7
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值