Rust 所有权特性详解
Rust 所有权特性详解
Rust 的所有权系统是其内存安全的核心机制之一。通过所有权规则,Rust 在编译时避免了常见的内存错误(如空指针、数据竞争等)。本文将从堆内存与栈内存、所有权规则、变量作用域、String
类型、内存分配、所有权移动、Clone
、栈内存的 Copy
、所有权与函数、返回值与作用域等角度详细介绍 Rust 的所有权特性,并通过综合示例展示这些知识点的实际应用。
1. 什么是堆内存和栈内存
-
栈内存:
- 后进先出(LIFO)的数据结构。
- 分配和释放速度快。
- 用于存储固定大小的数据(如基本类型,Rust的基本类型有哪些,他们存在堆内存还是栈内存?)。
-
堆内存:
- 动态分配的内存区域。
- 分配和释放速度较慢。
- 用于存储大小可变或生命周期不确定的数据(如
String
、Vec
)。
示例:栈内存与堆内存
fn main() {let x = 5; // x 存储在栈上let s = String::from("你好"); // s 的数据存储在堆上,指针存储在栈上println!("x: {}, s: {}", x, s);
}
输出:
x: 5, s: 你好
分析:
x
是基本类型,存储在栈上。s
是String
类型,数据存储在堆上,指针和长度等信息存储在栈上。
2. Rust 所有权的规则
Rust 的所有权规则如下:
- 每个值都有一个所有者。
- 同一时间只能有一个所有者。
- 当所有者离开作用域时,值会被自动释放。
示例:所有权规则
fn main() {let s1 = String::from("你好");let s2 = s1; // s1 的所有权转移到 s2// println!("{}", s1); // 错误:s1 不再拥有数据println!("s2: {}", s2);
}
输出:
s2: 你好
分析:
s1
的所有权在赋值给s2
后转移,s1
不再有效。
3. 变量的作用域
变量的作用域是从声明开始到当前块结束。
示例:变量作用域
fn main() {let s = String::from("你好"); // s 进入作用域{let inner_s = String::from("内部"); // inner_s 进入作用域println!("内部作用域: {}", inner_s);} // inner_s 离开作用域,内存被释放println!("外部作用域: {}", s);
} // s 离开作用域,内存被释放
输出:
内部作用域: 内部
外部作用域: 你好
分析:
inner_s
的作用域仅限于内部块。s
的作用域是整个main
函数。
4. String
类型
String
是 Rust 中动态分配的字符串类型,存储在堆上。
示例:String
类型
fn main() {let mut s = String::from("你好");s.push_str(", Rust!"); // 修改字符串println!("{}", s);
}
输出:
你好, Rust!
分析:
String
类型允许动态修改内容。
5. 内存分配
Rust 通过所有权系统自动管理堆内存的分配和释放。
示例:内存分配
fn main() {let s = String::from("你好"); // 分配堆内存println!("{}", s);
} // s 离开作用域,内存被释放
输出:
你好
分析:
String::from
分配堆内存。s
离开作用域时,内存被自动释放。
6. 所有权移动时变量和数据的状态变化
当所有权从一个变量移动到另一个变量时,原始变量将失效。
示例:所有权移动
fn main() {let s1 = String::from("hello");let s2 = s1; // s1 的所有权移动到 s2// println!("{}", s1); // 错误:s1 不再有效println!("s2: {}", s2);
}
输出:
s2: hello
分析:
-s1
的指针存在栈内存,栈内存的value
指向堆内存的第一个索引位置。
- 当执行
s2=s1
的时候,仅仅复制了栈内存上的数据,堆内存的内容不不变。如果堆内存上的数据非常大,复制的操作成本会无限增加!
Double Free
问题:当前s1、s2
都指向同一份数据,当这两个变量离开作用域时,他们会同时释放同一块内存,这就会引起Double Free安全问题
。为了确保内存安全,当执行到语句let s2=s1
时,Rust让s1
失效,也称之为将所有权转移给了s2
。s1
的所有权转移给s2
后,s1
失效(如下图所示)。
7. 作用域和内存分配
变量的作用域决定了其内存的生命周期。
示例:作用域和内存分配
fn main() {let s = String::from("你好"); // s 进入作用域,分配内存println!("{}", s);
} // s 离开作用域,内存被释放
输出:
你好
分析:
s
的作用域结束后,内存被自动释放。
8. Clone
Clone
允许显式复制堆上的数据。
示例:Clone
fn main() {let s1 = String::from("你好");let s2 = s1.clone(); // 显式复制数据println!("s1: {}, s2: {}", s1, s2);
}
输出:
s1: 你好, s2: 你好
分析:
clone
会复制堆上的数据,s1
和s2
都有效。
9. 栈内存的 Copy
基本类型实现了 Copy
trait,赋值时会复制值而不是移动所有权。
示例:栈内存的 Copy
fn main() {let x = 5;let y = x; // x 的值被复制println!("x: {}, y: {}", x, y);
}
输出:
x: 5, y: 5
分析:
x
和y
都有效,因为i32
实现了Copy
。
那么,哪些类型实现了
Copy
特质呢?你可以查看特定类型的文档来确认,但一般来说,任何由简单标量值组成的类型都可以实现Copy
,而任何需要分配内存或是某种形式的资源的类型则不能实现Copy
。以下是一些实现了Copy
的类型:
- 所有的整数类型,例如
u32
。 - 布尔类型
bool
,其值为true
和false
。 - 所有的浮点数类型,例如
f64
。 - 字符类型
char
。 - 元组,如果它们只包含同样实现了
Copy
的类型。例如,(i32, i32)
实现了Copy
,但(i32, String)
则没有。
10. 所有权和函数
将值传递给函数会转移所有权。
示例:所有权和函数
fn take_ownership(s: String) {println!("函数内部: {}", s);
} // s 离开作用域,内存被释放fn main() {let s = String::from("你好");take_ownership(s); // s 的所有权转移到函数// println!("{}", s); // 错误:s 不再有效
}
输出:
函数内部: 你好
分析:
s
的所有权在传递给函数后转移。
11. 返回值和作用域
函数可以通过返回值转移所有权。
示例:返回值和作用域
fn give_ownership() -> String {let s = String::from("你好");s // 返回 s,所有权转移给调用者
}fn main() {let s = give_ownership(); // s 获得所有权println!("{}", s);
}
输出:
你好
分析:
give_ownership
返回s
,所有权转移给main
函数中的s
。
综合示例
以下是一个综合示例,展示了所有权、作用域、Clone
、Copy
、函数与返回值的用法:
fn main() {// 栈内存的 Copylet x = 5;let y = x; // x 的值被复制println!("x: {}, y: {}", x, y);// 堆内存的所有权let s1 = String::from("你好");let s2 = s1.clone(); // 显式复制数据println!("s1: {}, s2: {}", s1, s2);// 所有权和函数let s3 = String::from("世界");take_ownership(s3); // s3 的所有权转移到函数// println!("{}", s3); // 错误:s3 不再有效// 返回值和作用域let s4 = give_ownership(); // s4 获得所有权println!("s4: {}", s4);
}fn take_ownership(s: String) {println!("函数内部: {}", s);
} // s 离开作用域,内存被释放fn give_ownership() -> String {let s = String::from("你好,世界");s // 返回 s,所有权转移给调用者
}
输出:
x: 5, y: 5
s1: 你好, s2: 你好
函数内部: 世界
s4: 你好,世界
分析:
x
和y
是基本类型,赋值时复制值。s1
和s2
是String
类型,使用clone
显式复制数据。s3
的所有权在传递给函数后转移。s4
通过函数返回值获得所有权。
总结
Rust 的所有权系统通过以下特性确保内存安全:
- 堆内存与栈内存:区分数据的存储位置。
- 所有权规则:确保每个值只有一个所有者。
- 作用域:决定变量的生命周期。
String
类型:动态分配的字符串。- 内存分配:自动管理堆内存。
- 所有权移动:转移所有权时原始变量失效。
Clone
:显式复制堆数据。- 栈内存的
Copy
:基本类型赋值时复制值。 - 所有权与函数:传递值会转移所有权。
- 返回值与作用域:通过返回值转移所有权。
通过合理使用这些特性,可以编写出高效且安全的 Rust 代码。
相关文章:

Rust 所有权特性详解
Rust 所有权特性详解 Rust 的所有权系统是其内存安全的核心机制之一。通过所有权规则,Rust 在编译时避免了常见的内存错误(如空指针、数据竞争等)。本文将从堆内存与栈内存、所有权规则、变量作用域、String 类型、内存分配、所有权移动、Cl…...
Gateway路由匹配规则详解
在微服务架构中,Gateway作为请求的入口,扮演着至关重要的角色。它不仅负责路由转发,还具备安全、监控、限流等多种功能。其中,路由匹配规则是Gateway的核心功能之一,它决定了请求如何被正确地转发到目标服务。本文将详…...
项目实操:windows批处理拉取git库和处理目录、文件
初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github:codetoys,所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的,可以在任何平台上使用。 源码指引:github源…...
前端开发知识梳理 - HTMLCSS
1. 盒模型 由内容区(content)、内边距(padding)、边框(border)和外边距(margin)组成。 (1)标准盒模型(box-sizing默认值, content-boxÿ…...
nginx中的proxy_set_header参数详解
在使用 Nginx 作为反向代理服务器时,proxy_set_header 指令扮演着至关重要的角色。它允许我们自定义请求头信息,将客户端请求传递给上游服务器时,添加或修改特定的信息,从而实现更灵活的代理功能。本文将深入探讨 proxy_set_heade…...
MapReduce是什么?
MapReduce 是一种编程模型,最初由 Google 提出,旨在处理大规模数据集。它是分布式计算的一个重要概念,通常用于处理海量数据并进行并行计算。MapReduce的基本思想是将计算任务分解为两个阶段:Map 阶段和 Reduce 阶段。 Map 阶段&a…...

Text2Sql:开启自然语言与数据库交互新时代(3030)
一、Text2Sql 简介 在当今数字化时代,数据处理和分析的需求日益增长。对于众多非技术专业人员而言,数据库操作的复杂性常常成为他们获取所需信息的障碍。而 Text2Sql 技术的出现,为这一问题提供了有效的解决方案。 Text2Sql,即文…...

《图解设计模式》笔记(五)一致性
十一、Composite模式:容器与内容的一致性 像文件夹与文件一样,文件夹中可以放子文件夹与文件,再比如容器中可以放更小的容器和具体内容。 Composite模式:使容器与内容具有一致性,创造出递归结构。 Composite&#x…...

华为支付-免密支付接入免密代扣说明
免密代扣包括支付并签约以及签约代扣场景。 开发者接入免密支付前需先申请开通签约代扣产品(即申请配置免密代扣模板及协议模板ID)。 华为支付以模板维度管理每一个代扣扣费服务,主要组成要素如下: 接入免密支付需注意&#x…...
React组件中的列表渲染与分隔符处理技巧
React组件中的列表渲染与分隔符处理技巧 摘要问题背景解决方案分析方案一:数组拼接法方案二:Fragment组件方案三:动态生成key 关键技术点1. key的使用原则2. Fragment组件3. 性能优化 实战演练挑战1:动态分隔符样式挑战2ÿ…...

【Pytorch和Keras】使用transformer库进行图像分类
目录 一、环境准备二、基于Pytorch的预训练模型1、准备数据集2、加载预训练模型3、 使用pytorch进行模型构建 三、基于keras的预训练模型四、模型测试五、参考 现在大多数的模型都会上传到huggface平台进行统一的管理,transformer库能关联到huggface中对应的模型&am…...
快速了解 c++ 异常处理 基础知识
相关代码概览: #include<stdexcept>std::runtime_errorcatch (const std::runtime_error& e) e.what() 相信大家一定见过这些代码,那么这些代码具体什么意思呢?我们一起来看一下 知识精讲: 异常处理是C中非常重要…...

deepseek API 调用-python
【1】创建 API keys 【2】安装openai SDK pip3 install openai 【3】代码: https://download.csdn.net/download/notfindjob/90343352...
玩转Gin框架:Golang使用Gin完成登录流程
文章目录 背景基于Token认证机制简介常见的Token类型Token的生成和验证在项目工程里创建jwt.go文件根目录新建.env文件 创建登录接口 /loginToken认证机制的优点 背景 登录流程,相信大家都很熟悉的。传统网站采用session后端验证登录状态,大致流程如下&…...

Linux学习笔记16---高精度延时实验
延时函数是很常用的 API 函数,在前面的实验中我们使用循环来实现延时函数,但是使用循环来实现的延时函数不准确,误差会很大。虽然使用到延时函数的地方精度要求都不会很严格( 要求严格的话就使用硬件定时器了 ) ,但是延时函数肯定…...

vue2:如何动态控制el-form-item之间的行间距
需求 某页面有查看和编辑两种状态: 编辑: 查看: 可以看到,查看时,行间距太大导致页面不紧凑,所以希望缩小查看是的行间距。 行间距设置 行间距通常是通过 CSS 的 margin 或 padding 属性来控制的。在 Element UI 的样式表中,.el-form-item 的下边距(margin-bottom)…...

deepseek从网络拓扑图生成说明文字实例
deepseek对话页面中输入问题指令: 我是安全测评工程师,正在撰写系统测评报告,现在需要对系统网络架构进行详细说明,请根据附件网络拓扑图输出详细说明文字。用总分的段落结构,先介绍各网络区域,再介绍网络…...

两种文件类型(pdf/图片)打印A4半张纸方法
环境:windows10、Adobe Reader XI v11.0.23 Pdf: 1.把内容由横排变为纵排: 2.点击打印按钮: 3.选择打印页范围和多页: 4.内容打印在纸张上部 图片: 1.右键图片点击打印: 2.选择打印类型: 3.打印配置&am…...

HTB:UnderPass[WriteUP]
目录 连接至HTB服务器并启动靶机 信息收集 使用rustscan对靶机TCP端口进行开放扫描 使用nmap对靶机TCP开放端口进行脚本、服务扫描 使用nmap对靶机TCP开放端口进行漏洞、系统扫描 使用nmap对靶机常用UDP端口进行开放扫描 使用nmap对靶机UDP开放端口进行脚本、服务扫描 …...

【deepseek实战】绿色好用,不断网
前言 最佳deepseek火热网络,我也开发一款windows的电脑端,接入了deepseek,基本是复刻了网页端,还加入一些特色功能。 助力国内AI,发出自己的热量 说一下开发过程和内容的使用吧。 目录 一、介绍 二、具体工作 1.1、引…...
[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解
突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 安全措施依赖问题 GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...
vscode里如何用git
打开vs终端执行如下: 1 初始化 Git 仓库(如果尚未初始化) git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...
rknn优化教程(二)
文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK,开始写第二篇的内容了。这篇博客主要能写一下: 如何给一些三方库按照xmake方式进行封装,供调用如何按…...
基于Uniapp开发HarmonyOS 5.0旅游应用技术实践
一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架,支持"一次开发,多端部署",可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务,为旅游应用带来…...
macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用
文章目录 问题现象问题原因解决办法 问题现象 macOS启动台(Launchpad)多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显,都是Google家的办公全家桶。这些应用并不是通过独立安装的…...
OkHttp 中实现断点续传 demo
在 OkHttp 中实现断点续传主要通过以下步骤完成,核心是利用 HTTP 协议的 Range 请求头指定下载范围: 实现原理 Range 请求头:向服务器请求文件的特定字节范围(如 Range: bytes1024-) 本地文件记录:保存已…...

【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)
🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...

pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...
Linux离线(zip方式)安装docker
目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1:修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本:CentOS 7 64位 内核版本:3.10.0 相关命令: uname -rcat /etc/os-rele…...
Java数值运算常见陷阱与规避方法
整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...