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)。

所有权规则

  1. Rust 中的每一个值都有一个 所有者owner)。
  2. 值在任一时刻有且只有一个所有者。
  3. 当所有者(变量)离开作用域,这个值将被丢弃。

变量的作用域

字符串字面值是被硬编码进程序里的字符串值,不可变,存储在栈当中,并且当离开作用域时被移出栈。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

两个数据指针指向了同一位置。这就有了一个问题:当 xy 离开作用域,它们都会尝试释放相同的内存。这是一个叫做 二次释放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,它的值是 truefalse
  • 所有浮点数类型,比如 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`
}

由此可见,变量在函数调用过程中,作用域会随着函数传入变量,函数返回变量发生变化,无法顺畅的在函数调用后继续使用变量。

这里就引入了引用的概念,可以使用引用实现丝滑的使用变量和函数。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/470741.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ctfshow-web入门-SSTI(web361-web368)上

目录 1、web361 2、web362 3、web363 4、web364 5、web365 6、web366 7、web367 8、web368 1、web361 测试一下存在 SSTI 注入 方法很多 (1)使用子类可以直接调用的函数来打 payload1: ?name{{.__class__.__base__.__subclasses__…

Axure网络短剧APP端原型图,竖屏微剧视频模版40页

作品概况 页面数量:共 40 页 使用软件:Axure RP 9 及以上,非软件无源码 适用领域:短剧、微短剧、竖屏视频 作品特色 本作品为网络短剧APP的Axure原型设计图,定位属于免费短剧软件,类似红果短剧、河马剧场…

如何从头开始构建神经网络?(附教程)

随着流行的深度学习框架的出现,如 TensorFlow、Keras、PyTorch 以及其他类似库,学习神经网络对于新手来说变得更加便捷。虽然这些框架可以让你在几分钟内解决最复杂的计算任务,但它们并不要求你理解背后所有需求的核心概念和直觉。如果你知道…

JS 实现SSE通讯和了解SSE通讯

SSE 介绍: Server-Sent Events(SSE)是一种用于实现服务器向客户端实时推送数据的Web技术。与传统的轮询和长轮询相比,SSE提供了更高效和实时的数据推送机制。 SSE基于HTTP协议,允许服务器将数据以事件流(…

HTML之表单学习记录

如果一个页面仅仅供用户浏览,那就是静态页面。如果这个页面还能实现与服务器进行数据交互(像注册登录、话费充值、评论交流)​,那就是动态页面。表单是我们接触动态页面的第一步。其中表单最重要的作用就是:在浏览器端…

WPF学习之路,控件的只读、是否可以、是否可见属性控制

C#的控件学习之控件属性操作 控件的只读、是否可以、是否可见,是三个重要的参数,在很多表单、列表中都有用到,正常表单控制可以在父层主键控制参数是否可以编辑和可见,但是遇到个别字段需要单独控制时,可以在初始化wi…

three.js 杂记

clip: 1: 着色器 #ifdef USE_CLIP_DISTANCE vec4 worldPosition modelMatrix * vec4( position, 1.0 ); gl_ClipDistance[ 0 ] worldPosition.x - sin( time ) * ( 0.5 ); #endif gl_Position projectionMatrix * modelViewMatrix * vec4( positio…

基于混合配准策略的多模态医学图像配准方法研究

摘要: 提出了一种由“粗”到“细”的混合配准策略,该配准策略吸取了以往配准方法的优点,且在细配阶段将基于特征的配准方法和基于灰度的配准方法结合在一起,提出了基于轮廓特征点集最大互信息的配准方法,从而在速度和精…

贪心算法入门(二)

相关文章 贪心算法入门(一)-CSDN博客 1.什么是贪心算法? 贪心算法是一种解决问题的策略,它将复杂的问题分解为若干个步骤,并在每一步都选择当前最优的解决方案,最终希望能得到全局最优解。这种策略的核心…

Autosar CP 基于CAN的时间同步规范导读

Autosar CP 基于CAN的时间同步规范主要用途 实现精确时间同步 提供了一种在CAN总线上准确分发时间信息的机制,确保连接到CAN网络的各个电子控制单元(ECU)能够共享精确的公共时间基准,对于需要精确时间协调的汽车系统功能&#xff…

前端常用布局模板39套,纯CSS实现布局

前端常用布局模板39套,纯CSS实现布局 说明 写博客、官网、管理后台都可以参考以下布局模板,实现模板布局的方式包含:flex、CSS、HTML5、Layout。 不需要下载积分,没有特殊库引用,不用安装任何插件,打开资源…

jmeter常用配置元件介绍总结之后置处理器

系列文章目录 安装jmeter jmeter常用配置元件介绍总结之后置处理器 8.后置处理器8.1.CSS/JQuery提取器8.2.JSON JMESPath Extractor8.3.JSON提取器8.4.正则表达式提取器8.5.边界提取器8.5.Debug PostProcessor8.6.XPath2 Extractor8.7.XPath提取器8.8.结果状态处理器 8.后置处理…

边缘计算在智能交通系统中的应用

💓 博客主页:瑕疵的CSDN主页 📝 Gitee主页:瑕疵的gitee主页 ⏩ 文章专栏:《热点资讯》 边缘计算在智能交通系统中的应用 边缘计算在智能交通系统中的应用 边缘计算在智能交通系统中的应用 引言 边缘计算概述 定义与原…

Vue 项目打包后环境变量丢失问题(清除缓存),区分.env和.env.*文件

Vue 项目打包后环境变量丢失问题(清除缓存),区分.env和.env.*文件 问题背景 今天在导报项目的时候遇到一个问题问题:在开发环境中一切正常,但在打包后的生产环境中,某些环境变量(如 VUE_APP_B…

十三、注解配置SpringMVC

文章目录 1. 创建初始化类,代替web.xml2. 创建SpringConfig配置类,代替spring的配置文件3. 创建WebConfig配置类,代替SpringMVC的配置文件4. 测试功能 1. 创建初始化类,代替web.xml 2. 创建SpringConfig配置类,代替spr…

(干货)Jenkins使用kubernetes插件连接k8s的认证方式

#Kubernetes插件简介 Kubernetes 插件的目的是能够使用 Kubernetes 配合,实现动态配置 Jenkins 代理(使用 Kubernetes 调度机制来优化负载),在执行 Jenkins Job 构建时,Jenkins Master 会在 kubernetes 中创建一个 Sla…

俏美韵从心出发,与女性一道为健康生活贡献力量

近期发布的《2025 全球食品与饮料》报告中显示,“回归本源”为2025年食品饮料赛道的趋势之一,消费者对于产品成分要求越来越严格,尤其是女性消费者,对成分是否自然,营养含量等方面越来越看重,俏美韵品牌从产…

区块链技术在慈善捐赠中的应用

💓 博客主页:瑕疵的CSDN主页 📝 Gitee主页:瑕疵的gitee主页 ⏩ 文章专栏:《热点资讯》 区块链技术在慈善捐赠中的应用 区块链技术在慈善捐赠中的应用 区块链技术在慈善捐赠中的应用 引言 区块链技术概述 定义与原理 发…

mongoDB的安装及使用

mongodb的安装参考: Centos系统中mongodb的安装详解_centos安装mongodb-CSDN博客 不要下载最新的版本,新的版本中mongo命令无法使用,也就是安装后不能通过mongo命令登录,我这里使用5.0.30版本; mongodb客户端demo: …

DNS面临的4大类共计11小类安全风险及防御措施

DNS在设计之初,并未考虑网络安全限制,导致了许多问题。DNS安全扩展(DNSSEC)协议的开发旨在解决DNS的安全漏洞,但其部署并不广泛,DNS仍面临各种攻击。接下来我们一起看下DNS都存在哪些安全攻击及缓解措施,旨在对DNS安全…