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

解锁数据库简洁之道:FastAPI与SQLModel实战指南

在构建现代Web应用程序时&#xff0c;与数据库的交互无疑是核心环节。虽然传统的数据库操作方式&#xff08;如直接编写SQL语句与psycopg2交互&#xff09;赋予了我们精细的控制权&#xff0c;但在面对日益复杂的业务逻辑和快速迭代的需求时&#xff0c;这种方式的开发效率和可…...

大模型多显卡多服务器并行计算方法与实践指南

一、分布式训练概述 大规模语言模型的训练通常需要分布式计算技术,以解决单机资源不足的问题。分布式训练主要分为两种模式: 数据并行:将数据分片到不同设备,每个设备拥有完整的模型副本 模型并行:将模型分割到不同设备,每个设备处理部分模型计算 现代大模型训练通常结合…...

实现弹窗随键盘上移居中

实现弹窗随键盘上移的核心思路 在Android中&#xff0c;可以通过监听键盘的显示和隐藏事件&#xff0c;动态调整弹窗的位置。关键点在于获取键盘高度&#xff0c;并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)

在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马&#xff08;服务器方面的&#xff09;的原理&#xff0c;连接&#xff0c;以及各种木马及连接工具的分享 文件木马&#xff1a;https://w…...

力扣热题100 k个一组反转链表题解

题目: 代码: func reverseKGroup(head *ListNode, k int) *ListNode {cur : headfor i : 0; i < k; i {if cur nil {return head}cur cur.Next}newHead : reverse(head, cur)head.Next reverseKGroup(cur, k)return newHead }func reverse(start, end *ListNode) *ListN…...

LRU 缓存机制详解与实现(Java版) + 力扣解决

&#x1f4cc; LRU 缓存机制详解与实现&#xff08;Java版&#xff09; 一、&#x1f4d6; 问题背景 在日常开发中&#xff0c;我们经常会使用 缓存&#xff08;Cache&#xff09; 来提升性能。但由于内存有限&#xff0c;缓存不可能无限增长&#xff0c;于是需要策略决定&am…...

【Linux】自动化构建-Make/Makefile

前言 上文我们讲到了Linux中的编译器gcc/g 【Linux】编译器gcc/g及其库的详细介绍-CSDN博客 本来我们将一个对于编译来说很重要的工具&#xff1a;make/makfile 1.背景 在一个工程中源文件不计其数&#xff0c;其按类型、功能、模块分别放在若干个目录中&#xff0c;mak…...

Sklearn 机器学习 缺失值处理 获取填充失值的统计值

💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 使用 Scikit-learn 处理缺失值并提取填充统计信息的完整指南 在机器学习项目中,数据清…...

C# winform教程(二)----checkbox

一、作用 提供一个用户选择或者不选的状态&#xff0c;这是一个可以多选的控件。 二、属性 其实功能大差不差&#xff0c;除了特殊的几个外&#xff0c;与button基本相同&#xff0c;所有说几个独有的 checkbox属性 名称内容含义appearance控件外观可以变成按钮形状checkali…...

高分辨率图像合成归一化流扩展

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 1 摘要 我们提出了STARFlow&#xff0c;一种基于归一化流的可扩展生成模型&#xff0c;它在高分辨率图像合成方面取得了强大的性能。STARFlow的主要构建块是Transformer自回归流&#xff08;TARFlow&am…...