当前位置: 首页 > news >正文

Rust 所有权特性详解

Rust 所有权特性详解

Rust 的所有权系统是其内存安全的核心机制之一。通过所有权规则,Rust 在编译时避免了常见的内存错误(如空指针、数据竞争等)。本文将从堆内存与栈内存、所有权规则、变量作用域、String 类型、内存分配、所有权移动、Clone、栈内存的 Copy、所有权与函数、返回值与作用域等角度详细介绍 Rust 的所有权特性,并通过综合示例展示这些知识点的实际应用。


1. 什么是堆内存和栈内存

  • 栈内存

    • 后进先出(LIFO)的数据结构。
    • 分配和释放速度快。
    • 用于存储固定大小的数据(如基本类型,Rust的基本类型有哪些,他们存在堆内存还是栈内存?)。
  • 堆内存

    • 动态分配的内存区域。
    • 分配和释放速度较慢。
    • 用于存储大小可变或生命周期不确定的数据(如 StringVec)。

示例:栈内存与堆内存

fn main() {let x = 5; // x 存储在栈上let s = String::from("你好"); // s 的数据存储在堆上,指针存储在栈上println!("x: {}, s: {}", x, s);
}

输出

x: 5, s: 你好

分析

  • x 是基本类型,存储在栈上。
  • sString 类型,数据存储在堆上,指针和长度等信息存储在栈上。

2. Rust 所有权的规则

Rust 的所有权规则如下:

  1. 每个值都有一个所有者。
  2. 同一时间只能有一个所有者。
  3. 当所有者离开作用域时,值会被自动释放。

示例:所有权规则

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 会复制堆上的数据,s1s2 都有效。

9. 栈内存的 Copy

基本类型实现了 Copy trait,赋值时会复制值而不是移动所有权。

示例:栈内存的 Copy

fn main() {let x = 5;let y = x; // x 的值被复制println!("x: {}, y: {}", x, y);
}

输出

x: 5, y: 5

分析

  • xy 都有效,因为 i32 实现了 Copy

那么,哪些类型实现了 Copy 特质呢?你可以查看特定类型的文档来确认,但一般来说,任何由简单标量值组成的类型都可以实现 Copy,而任何需要分配内存或是某种形式的资源的类型则不能实现 Copy。以下是一些实现了 Copy 的类型:

  • 所有的整数类型,例如 u32
  • 布尔类型 bool,其值为 truefalse
  • 所有的浮点数类型,例如 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

综合示例

以下是一个综合示例,展示了所有权、作用域、CloneCopy、函数与返回值的用法:

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: 你好,世界

分析

  1. xy 是基本类型,赋值时复制值。
  2. s1s2String 类型,使用 clone 显式复制数据。
  3. s3 的所有权在传递给函数后转移。
  4. s4 通过函数返回值获得所有权。

总结

Rust 的所有权系统通过以下特性确保内存安全:

  1. 堆内存与栈内存:区分数据的存储位置。
  2. 所有权规则:确保每个值只有一个所有者。
  3. 作用域:决定变量的生命周期。
  4. String 类型:动态分配的字符串。
  5. 内存分配:自动管理堆内存。
  6. 所有权移动:转移所有权时原始变量失效。
  7. Clone:显式复制堆数据。
  8. 栈内存的 Copy:基本类型赋值时复制值。
  9. 所有权与函数:传递值会转移所有权。
  10. 返回值与作用域:通过返回值转移所有权。

通过合理使用这些特性,可以编写出高效且安全的 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&#xff…...

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&#xff…...

【Pytorch和Keras】使用transformer库进行图像分类

目录 一、环境准备二、基于Pytorch的预训练模型1、准备数据集2、加载预训练模型3、 使用pytorch进行模型构建 三、基于keras的预训练模型四、模型测试五、参考 现在大多数的模型都会上传到huggface平台进行统一的管理,transformer库能关联到huggface中对应的模型&am…...

快速了解 c++ 异常处理 基础知识

相关代码概览&#xff1a; #include<stdexcept>std::runtime_errorcatch (const std::runtime_error& e) e.what() 相信大家一定见过这些代码&#xff0c;那么这些代码具体什么意思呢&#xff1f;我们一起来看一下 知识精讲&#xff1a; 异常处理是C中非常重要…...

deepseek API 调用-python

【1】创建 API keys 【2】安装openai SDK pip3 install openai 【3】代码&#xff1a; https://download.csdn.net/download/notfindjob/90343352...

玩转Gin框架:Golang使用Gin完成登录流程

文章目录 背景基于Token认证机制简介常见的Token类型Token的生成和验证在项目工程里创建jwt.go文件根目录新建.env文件 创建登录接口 /loginToken认证机制的优点 背景 登录流程&#xff0c;相信大家都很熟悉的。传统网站采用session后端验证登录状态&#xff0c;大致流程如下&…...

Linux学习笔记16---高精度延时实验

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

vue2:如何动态控制el-form-item之间的行间距

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

deepseek从网络拓扑图生成说明文字实例

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

两种文件类型(pdf/图片)打印A4半张纸方法

环境:windows10、Adobe Reader XI v11.0.23 Pdf: 1.把内容由横排变为纵排&#xff1a; 2.点击打印按钮&#xff1a; 3.选择打印页范围和多页&#xff1a; 4.内容打印在纸张上部 图片&#xff1a; 1.右键图片点击打印&#xff1a; 2.选择打印类型&#xff1a; 3.打印配置&am…...

HTB:UnderPass[WriteUP]

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

【deepseek实战】绿色好用,不断网

前言 最佳deepseek火热网络&#xff0c;我也开发一款windows的电脑端&#xff0c;接入了deepseek&#xff0c;基本是复刻了网页端&#xff0c;还加入一些特色功能。 助力国内AI&#xff0c;发出自己的热量 说一下开发过程和内容的使用吧。 目录 一、介绍 二、具体工作 1.1、引…...

[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解

突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 ​安全措施依赖问题​ GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...

vscode里如何用git

打开vs终端执行如下&#xff1a; 1 初始化 Git 仓库&#xff08;如果尚未初始化&#xff09; 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&#xff0c;开始写第二篇的内容了。这篇博客主要能写一下&#xff1a; 如何给一些三方库按照xmake方式进行封装&#xff0c;供调用如何按…...

基于Uniapp开发HarmonyOS 5.0旅游应用技术实践

一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架&#xff0c;支持"一次开发&#xff0c;多端部署"&#xff0c;可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务&#xff0c;为旅游应用带来&#xf…...

macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用

文章目录 问题现象问题原因解决办法 问题现象 macOS启动台&#xff08;Launchpad&#xff09;多出来了&#xff1a;Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显&#xff0c;都是Google家的办公全家桶。这些应用并不是通过独立安装的…...

OkHttp 中实现断点续传 demo

在 OkHttp 中实现断点续传主要通过以下步骤完成&#xff0c;核心是利用 HTTP 协议的 Range 请求头指定下载范围&#xff1a; 实现原理 Range 请求头&#xff1a;向服务器请求文件的特定字节范围&#xff08;如 Range: bytes1024-&#xff09; 本地文件记录&#xff1a;保存已…...

【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关&#xff0…...

Linux离线(zip方式)安装docker

目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1&#xff1a;修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本&#xff1a;CentOS 7 64位 内核版本&#xff1a;3.10.0 相关命令&#xff1a; uname -rcat /etc/os-rele…...

Java数值运算常见陷阱与规避方法

整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...