当前位置: 首页 > 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、引…...

【Latex】Windows/Ubuntu 绘制 eps 矢量图通用方法(drawio),支持插入 Latex 数学公式

一直感觉 Visio 或者 PPT 中 Mathtype 对 latex 公式渲染效果不好&#xff0c;且在 Ubuntu 下的支持不好&#xff0c;最近重新调研发现一个好用的工具 drawio。 在线使用 https://app.diagrams.net/?srcabout 也有桌面版的应用&#xff0c;Windows 就下载 exe 安装器&#x…...

如何生成和制作PDF文件

在数字化办公的今天&#xff0c;PDF文件已经成为我们工作和学习中不可或缺的一部分。无论是合同、报告、简历&#xff0c;还是电子书、表单&#xff0c;PDF格式都以其跨平台兼容性、不可编辑性和清晰的排版而被广泛使用。但你是否知道&#xff0c;生成和制作PDF文件其实并不复杂…...

Web前端基础:JavaScript

1.JS核心语法 1.1 JS引入方式 第一种方式&#xff1a;内部脚本&#xff0c;将JS代码定义在HTML页面中 JavaScript代码必须位于<script></script>标签之间在HTML文档中&#xff0c;可以在任意地方&#xff0c;放置任意数量的<script></script>一般会把…...

bat批量去掉本文件夹中的文件扩展名

本文本夹内 批量去掉本文件夹中的文件扩展名 假如你有一些文件&#xff0c;你想去掉他们的扩展名 有没有方便的办法呢 今天我们就分享一种办法。 下面&#xff0c;就来看看吧。 首先我们新建一个记事本&#xff0c;把名字改为&#xff0c;批量去掉本文件夹中的文件扩展名.txt 然…...

机器学习监督学习实战五:六种算法对声呐回波信号进行分类

本项目基于UCI的声呐目标识别数据集&#xff08;Sonar, Mines vs. Rocks&#xff09;&#xff0c;通过10种机器学习算法比较&#xff0c;发现集成学习方法表现最优。研究首先对60个声呐能量特征进行可视化分析&#xff08;分布直方图、相关性矩阵&#xff09;&#xff0c;对比了…...

如何在mac上安装podman

安装 Podman 在 macOS 上 在 macOS 上安装 Podman 需要使用 Podman 的桌面客户端工具 Podman Desktop 或通过 Homebrew 安装命令行工具。 使用 Homebrew 安装 Podman&#xff1a; (base) ninjamacninjamacdeMacBook-Air shell % brew install podman > Auto-updating Hom…...

宝塔安装配置FRP

FRP&#xff08;Fast Reverse Proxy&#xff09;作为一款高性能的反向代理应用&#xff0c;能够帮助我们轻松实现内网穿透&#xff0c;将内网服务暴露到公网&#xff0c;满足远程访问、开发调试等多种需求。宝塔面板以其简洁易用的界面和强大的功能&#xff0c;成为众多站长和开…...

C# 中替换多层级数据的 Id 和 ParentId,保持主从或父子关系不变

在C#中替换多层级数据的Id和ParentId&#xff0c;同时保持父子关系不变&#xff0c;可以通过以下步骤实现&#xff1a; 创建旧Id到新Id的映射&#xff1a;遍历所有节点&#xff0c;为每个旧Id生成唯一的新Id&#xff0c;并存储在字典中。 替换节点的Id和ParentId&#xff1a;…...

Qt/C++学习系列之Excel使用记录

Qt/C学习系列之Excel使用记录 前言The process was ended forcefully.解决方式断点查语句问题 总结 前言 在项目中解析条目达50多条&#xff0c;并且都需要将对应的结果进行显示。为了将结果显示的更加清晰&#xff0c;考虑采用QTableWidget进行表格设置&#xff0c;而在使用过…...

【软件工具】批量OCR指定区域图片自动识别内容重命名软件使用教程及注意事项

批量OCR指定区域图片自动识别内容重命名软件使用教程及注意事项 1、操作步骤1-5&#xff1a; 安装与启动&#xff1a;安装成功后&#xff0c;在桌面或开始菜单找到软件图标&#xff0c;双击启动。 导入图片&#xff1a;进入软件主界面&#xff0c;点击 “导入图片” 按钮&a…...