Rust 所有权机制
Rust 所有权机制
本文示例代码地址
所有权是Rust中最独特的特性,它让Rust无需GC就可以保证内存安全。
什么是所有权?
所有权(ownership)是 Rust 用于如何管理内存的一组规则。所有程序都必须管理其运行时使用计算机内存的方式。一些语言中具有垃圾回收机制,在程序运行时有规律地寻找不再使用的内存;在另一些语言中,程序员必须亲自分配和释放内存。Rust 则选择了第三种方式:通过所有权系统管理内存,编译器在编译时会根据一系列的规则进行检查。如果违反了任何这些规则,程序都不能编译。在运行时,所有权系统的任何功能都不会减慢程序。
摘自:什么是所有权
栈内存和堆内存(Stack vs Heap)
在Rust语言中,一个值在栈内存还是堆内存,对语言的行为和编码时要为此做的操作有更大的影响。
栈和堆都是代码在运行时可供使用的内存,但是它们的结构不同。栈以放入值的顺序存储值并以相反顺序取出值。这也被称作 后进先出(last in, first out)。
增加数据叫做 进栈(pushing onto the stack),而移出数据叫做 出栈(popping off the stack)。栈中的所有数据都必须占用已知且固定的大小。在编译时大小未知或大小可能变化的数据,要改为存储在堆上。 堆是缺乏组织的:当向堆放入数据时,你要请求一定大小的空间。内存分配器(memory allocator)在堆的某处找到一块足够大的空位,把它标记为已使用,并返回一个表示该位置地址的 指针(pointer)。这个过程称作 在堆上分配内存(allocating on the heap),有时简称为 “分配”(allocating)。
所有权规则
- Rust 中的每一个值都有一个 所有者(owner)。
- 值在任一时刻有且只有一个所有者。
- 当所有者(变量)离开作用域,这个值将被丢弃。
变量的作用域
字符串字面值是被硬编码进程序里的字符串值,不可变,存储在栈当中,并且当离开作用域时被移出栈。String类型存储在堆当中,可变,所以在这里更适合用来讨论所有权机制以及变量作用域的情况。
Rust释放无用内存的策略:内存在拥有它的变量离开作用域后就被自动释放。
使用大括号可以自定义变量的作用域范围,以下分别是字符串字面值和String类型的关于作用域的示例:
// 字符串字面值
{ // s 在这里无效,它尚未声明let s = "hello"; // 从此处起,s 是有效的// 使用 s
} // 此作用域已结束,s 不再有效
// String 类型
{let s = String::from("hello"); // 从此处起,s 是有效的// 使用 s
} // 此作用域已结束,// s 不再有效
String类型
演示String类型的变量作用域,y和x 指向堆中的同一块内存区域,Rust为了保证内存安全,如果x赋值给了y,就会结束x的作用域,代码示例:
let x = String::from("5");let y = x;println!("x:{}", x);
执行结果:
|
11 | let x = String::from("5");| - move occurs because `x` has type `String`, which does not implement the `Copy` trait
12 | let y = x;| - value moved here
13 | println!("x:{}", x);| ^ value borrowed here after move
两个数据指针指向了同一位置。这就有了一个问题:当 x
和 y
离开作用域,它们都会尝试释放相同的内存。这是一个叫做 二次释放(double free)的错误,也是之前提到过的内存安全性 bug 之一。两次释放(相同)内存会导致内存污染,它可能会导致潜在的安全漏洞。
为了确保内存安全,在 let y = x;
之后,Rust 认为 x
不再有效,因此 Rust 不需要在 x
离开作用域后清理任何东西。此时,只有 s2
是有效的,当其离开作用域,它就释放自己的内存。
clone
如果我们 确实 需要深度复制 String
中堆上的数据,而不仅仅是栈上的数据,可以使用一个叫做 clone
的通用函数。
let x = String::from("5");let y = x.clone();println!("x:{}", x);
存放在栈上的整形变量,经过移动后,依然有效。因为默认实现了 Copy
trait,代码示例:
let x = 5;let y = x;println!("x:{}", x
执行结果:
x:5
如果一个类型实现了 Copy
trait,那么一个旧的变量在将其赋值给其他变量后仍然可用。
如下是一些 Copy
的类型:
- 所有整数类型,比如
u32
。 - 布尔类型,
bool
,它的值是true
和false
。 - 所有浮点数类型,比如
f64
。 - 字符类型,
char
。 - 元组,当且仅当其包含的类型也都实现
Copy
的时候。比如,(i32, i32)
实现了Copy
,但(i32, String)
就没有。
所有权与函数
和变量之间进行赋值类似,变量在函数中的移动也会发生作用域的变化,代码示例:
fn takes_ownership(some_string: String) {println!("{}", some_string);
}#[cfg(test)]
mod tests {use super::*;#[test]fn test_takes_ownership() {let s = String::from("hello");takes_ownership(s);println!("s:{}", s); // 这里发生报错}}
执行 test_takes_ownership()
error[E0382]: borrow of moved value: `s`--> crates/ownership_demo/src/lib.rs:14:26|
12 | let s = String::from("hello");| - move occurs because `s` has type `String`, which does not implement the `Copy` trait
13 | takes_ownership(s);| - value moved here
14 | println!("s:{}", s);| ^ value borrowed here after move|
note: consider changing this parameter type in function `takes_ownership` to borrow instead if owning the value isn't necessary--> crates/ownership_demo/src/lib.rs:1:33|
1 | fn takes_ownership(some_string: String) {| --------------- ^^^^^^ this parameter takes ownership of the value| || in this function= note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider cloning the value if the performance cost is acceptable|
13 | takes_ownership(s.clone());|
当 s
传入到函数当中时,s的作用域就到了函数中,函数后面再获取s将不被允许,发生编译错误,执行也会发生异常。
但是对于类型是i32类型的参数,情况不同,代码示例如下:
fn takes_ownership(some_string: String) {println!("{}", some_string);
}fn takes_ownership_i32(value: i32) {println!("{}", value);
}#[cfg(test)]
mod tests {use super::*;#[test]fn test_takes_ownership() {let s = String::from("hello");takes_ownership(s);//println!("s:{}", s);}#[test]fn test_takes_ownership_i32() {let value = 12;takes_ownership_i32(value);println!("value:{}", value);}}
执行 test_takes_ownership_i32()
running 1 test
test tests::test_takes_ownership_i32 ... oksuccesses:---- tests::test_takes_ownership_i32 stdout ----
12
value:12successes:tests::test_takes_ownership_i32
同理,如果一个类型实现了 Copy
trait,那么一个变量在将其传递到函数当中后,函数之后可以再读取该变量。
返回值与作用域
返回值也可以转移所有权。
// 返回一个String 类型
fn gives_ownership() -> String {let s = String::from("hello"); // s进入作用域s // s 返回给调用函数
}#[test]
fn test_gives_ownership() {let s = gives_ownership(); // s进入作用域takes_ownership(s); // s作用域移到takes_ownership函数中println!("s:{}", s); // s 已经被移走,这里会报错 borrow of moved value: `s`
}
由此可见,变量在函数调用过程中,作用域会随着函数传入变量,函数返回变量发生变化,无法顺畅的在函数调用后继续使用变量。
这里就引入了引用的概念,可以使用引用实现丝滑的使用变量和函数。
相关文章:
Rust 所有权机制
Rust 所有权机制 本文示例代码地址 所有权是Rust中最独特的特性,它让Rust无需GC就可以保证内存安全。 什么是所有权? 所有权(ownership)是 Rust 用于如何管理内存的一组规则。所有程序都必须管理其运行时使用计算机内存的方式…...
Pwn VM writeup
国赛期间,做了一个很有意思的pwn题,顺便学了一下现在常见的pwn的板子题是什么样子的,这里做一下记录 Magic VM 题目逻辑 题目本身其实非常的有趣,它实现了一个简易流水线的功能,程序中包含四个结构体,其中三…...

LSTM(长短期记忆网络)详解
1️⃣ LSTM介绍 标准的RNN存在梯度消失和梯度爆炸问题,无法捕捉长期依赖关系。那么如何理解这个长期依赖关系呢? 例如,有一个语言模型基于先前的词来预测下一个词,我们有一句话 “the clouds are in the sky”,基于&…...

机器学习 贝叶斯公式
这是条件概率的计算公式 𝑃(𝐴|𝐵)𝑃(B|A)𝑃(𝐴)/𝑃(𝐵) 全概率公式 𝑃(𝐵)𝑃(𝐵|𝐴)𝑃(𝐴)&am…...

Scala-注释、标识符、变量与常量-用法详解
Scala Scala-变量和数据类型-用法详解 Scala一、注释二、标识符规范三、变量和常量1. 变量(var)2. 常量(val)3. 类型推断与显式声明4. var 和 val 的区别5. Scala与Java对比Tips: 各位看客老爷万福金安,一键…...

大数据学习14之Scala面向对象--至简原则
1.类和对象 1.1基本概念 面向对象(Object Oriented)是一种编程思想,面向对象主要是把事物给对象化,包括其属性和行为。面向对象编程更贴近实际生活的思想,总体来说面向对象的底层还是面向过程,面向过程抽象…...

docker 安装之 windows安装
文章目录 1: 在Windows安装Docker报19044版本错误的时候,请大家下载4.24.1之前的版本(含4.24.1)2: Desktop-WSL kernel version too low3: docker-compose 安装 (v2.21.0) 1: 在Windows安装Docker报19044版本错误的时候,请大家下载…...

JS 实现游戏流畅移动与按键立即响应
AWSD 按键移动 <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title><style>.box1 {width: 400px;height: 400px;background: yellowgreen;margin: 0 auto;position: relative;}.box2 {width: 50px;height:…...

LabVIEW大数据处理
在物联网、工业4.0和科学实验中,大数据处理需求逐年上升。LabVIEW作为一款图形化编程语言,凭借其强大的数据采集和分析能力,广泛应用于实时数据处理和控制系统中。然而,在面对大数据处理时,LabVIEW也存在一些注意事项。…...

NVR录像机汇聚管理EasyNVR多品牌NVR管理工具视频汇聚技术在智慧安防监控中的应用与优势
随着信息技术的快速发展和数字化时代的到来,安防监控领域也在不断进行技术创新和突破。NVR管理平台EasyNVR作为视频汇聚技术的领先者,凭借其强大的视频处理、汇聚与融合能力,展现出了在安防监控领域巨大的应用潜力和价值。本文将详细介绍Easy…...

海思3403对RTSP进行目标检测
1.概述 主要功能是调过live555 testRTSPClient 简单封装的rtsp客户端库,拉取RTSP流,然后调过3403的VDEC模块进行解码,送个NPU进行目标检测,输出到hdmi,这样保证了开发没有sensor的时候可以识别其它摄像头的视频流&…...

Vue之插槽(slot)
插槽是vue中的一个非常强大且灵活的功能,在写组件时,可以为组件的使用者预留一些可以自定义内容的占位符。通过插槽,可以极大提高组件的客服用和灵活性。 插槽大体可以分为三类:默认插槽,具名插槽和作用域插槽。 下面…...

分布式服务高可用实现:复制
分布式服务高可用实现:复制 1. 为什么需要复制 我们可以考虑如下问题: 当数据量、读取或写入负载已经超过了当前服务器的处理能力,如何实现负载均衡?希望在单台服务器出现故障时仍能继续工作,这该如何实现ÿ…...

基于yolov8、yolov5的车型检测识别系统(含UI界面、训练好的模型、Python代码、数据集)
摘要:车型识别在交通管理、智能监控和车辆管理中起着至关重要的作用,不仅能帮助相关部门快速识别车辆类型,还为自动化交通监控提供了可靠的数据支撑。本文介绍了一款基于YOLOv8、YOLOv5等深度学习框架的车型识别模型,该模型使用了…...

机器学习—决定下一步做什么
现在已经看到了很多不同的学习算法,包括线性回归、逻辑回归甚至深度学习或神经网络。 关于如何构建机器学习系统的一些建议 假设你已经实现了正则化线性回归来预测房价,所以你有通常的学习算法的成本函数平方误差加上这个正则化项,但是如果…...
Java Optional详解:避免空指针异常的优雅方式
在 Java 编程中,空指针异常(NullPointerException)一直是困扰开发者的常见问题之一。为了更安全、优雅地处理可能为空的值,Java 8 引入了 Optional 类。Optional 提供了一种函数式的方式来表示一个值可能存在或不存在,…...
SpringBoot开发——整合EasyExcel实现百万级数据导入导出功能
文章目录 一、EasyExcel 框架及特性介绍二、实现步骤1、项目创建及依赖配置(pom.xml)2、项目文件结构3、配置文件(application.yml)4、启动类 Application.java5、配置类 EasyExcelConfig.java6、服务接口定义及实现 ExcelService.java7、控制器类 ExcelController.java8、…...
AcWing 1097 池塘计数 flood fill bfs搜索
代码 #include <bits/stdc.h> using namespace std;const int N 1010, M N * N;typedef pair<int, int> PII;int n, m;char g[N][N]; bool st[N][N]; PII q[M];void bfs (int xx, int yy) {int hh 0, tt -1;q[ tt] {xx, yy};st[xx][yy] true;while (hh <…...

R门 - rust第一课陈天 -内存知识学习笔记
内存 #mermaid-svg-1NFTUW33mcI2cBGB {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-1NFTUW33mcI2cBGB .error-icon{fill:#552222;}#mermaid-svg-1NFTUW33mcI2cBGB .error-text{fill:#552222;stroke:#552222;}#merm…...

java itext后端生成pdf导出
public CustomApiResult<String> exportPdf(HttpServletRequest request, HttpServletResponse response) throws IOException {// 防止日志记录获取session异常request.getSession();// 设置编码格式response.setContentType("application/pdf;charsetUTF-8")…...

利用最小二乘法找圆心和半径
#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...
CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型
CVPR 2025 | MIMO:支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题:MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者:Yanyuan Chen, Dexuan Xu, Yu Hu…...

23-Oracle 23 ai 区块链表(Blockchain Table)
小伙伴有没有在金融强合规的领域中遇见,必须要保持数据不可变,管理员都无法修改和留痕的要求。比如医疗的电子病历中,影像检查检验结果不可篡改行的,药品追溯过程中数据只可插入无法删除的特性需求;登录日志、修改日志…...
IGP(Interior Gateway Protocol,内部网关协议)
IGP(Interior Gateway Protocol,内部网关协议) 是一种用于在一个自治系统(AS)内部传递路由信息的路由协议,主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...
pam_env.so模块配置解析
在PAM(Pluggable Authentication Modules)配置中, /etc/pam.d/su 文件相关配置含义如下: 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块,负责验证用户身份&am…...
postgresql|数据库|只读用户的创建和删除(备忘)
CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...
TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案
一、TRS收益互换的本质与业务逻辑 (一)概念解析 TRS(Total Return Swap)收益互换是一种金融衍生工具,指交易双方约定在未来一定期限内,基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...
Spring AI 入门:Java 开发者的生成式 AI 实践之路
一、Spring AI 简介 在人工智能技术快速迭代的今天,Spring AI 作为 Spring 生态系统的新生力量,正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务(如 OpenAI、Anthropic)的无缝对接&…...
【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统
目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索(基于物理空间 广播范围)2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...