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

rust所有权

一、堆和栈

栈和堆都是程序运行时使用的内存,但是它们的结构不同。

1.栈
栈,英文是stack。是内存的一段区域。
栈是后进先出形式的。就像薯片桶,先放进去的一片只能后拿出来。
栈上存储的数据大小必须是已知且固定的。也就是说如果一个变量或数据要放到栈上,那么它的大小在编译是就必须是明确的。
例如,类型为i32的变量,它的大小是固定4个字节。

2.堆
堆,英文是heap。是内存的另一段区域。堆内存也叫做资源。
堆是缺乏组织的:当向堆放入数据时,你要请求一定大小的空间。内存分配器在堆的某处找到一块足够大的空位,把它标记为已使用,并返回一个表示该位置地址的指针。这个过程称作在堆上分配内存。因为指针大小是已知且固定的,所以可以将该指针存储在栈上,不过当需要实际数据时,必须去指针指向的内存读取数据。就像一个围棋棋盘,你可以把一枚棋子放到任意可以放得下的位置。
在编译时大小未知或大小可能变化的数据,要存储在堆上。
堆不受系统管理,由用户自己管理,因此,使用不当,内存溢出的可能性就大大增加了。

Rust与其他语言的区别
(1)指针超出作用域会自动释放堆内存:
对于简单类型的栈内存(如int)超出作用域后自动释放,这个功能各个语言都有。而对于new出来的堆内存,在c/c++中要手动释放,在java中要委托垃圾回收释放。而垃圾回收不是实时的,会影响性能,手动释放又总会有人忘记释放。而Rust对栈内存和堆内存一视同仁,超出作用域一律自动释放,相当于自动delete指针。所以rust避免了内存泄漏。
在作用域结束时释放资源的模式称作资源获取即初始化(Resource Acquisition Is Initialization (RAII))。
rust强制使用raii,所以任何对象在离开作用域时,它的析构函数就被调用,然后它占有的资源就被释放。
这避免了资源泄漏,所以你再也不用手动释放内存或者担心内存泄漏。
(2)所有权:
某段内存只能被最后的变量名所有,前面声明过的变量都作废,这样一段内存就只有一个所有者,只有所有者可以释放这块内存。这有效避免了被多个变量释放的问题,而且该操作是在编译期的,这可在编译期就能避免空指针问题。比如c++中,a和b指向同一块内存,如果delete a之后,再delete b就会出错,而rust中不会出现这种问题。

二、所有权

(一)所有权是什么
所有权是指对内存资源的控制权和管理权。
在Rust中,每个值都有一个唯一的所有者。定义一个变量就是声明这个值由这个变量所有。所有的值最终都要存储在一块内存上,变量拥有这个值其实是拥有这块内存。栈内存所有者就是声明时的变量,而堆内存所有者是分配返回的指针。
只有所有者才能释放这块内存,其他人不能释放这块内存。
当所有者超出作用域时,会自动释放这块内存。

所有权的规则:
1.Rust中的每一个值都有一个所有者。
2.值在任一时刻有且只有一个所有者。
3.当所有者离开作用域,这个值将被丢弃。

比如,
现在可以把Box当成一个指针,后面章节再讲解Box用法。

fn create_box() {let _box1 = Box::new(3i32);// 在堆上分配一个整型数据// `_box1` 在这里被销毁,内存得到释放。如果是c++,就得手动delete,否则就会内存泄漏。这就是区别。
}
fn main() {let _box2 = Box::new(5i32);// 在堆上分配一个整型数据// 嵌套作用域:{let _box3 = Box::new(4i32);// 在堆上分配一个整型数据// `_box3` 在这里被销毁,内存得到释放}// 创建一大堆 box 完全不需要手动释放内存!for _ in 0u32..1000 {create_box();}// `_box2` 在这里被销毁,内存得到释放
}

(二)转让所有权
所有权可以转让。转让所有权也叫move。
就是由新变量拥有内存,旧变量变成无效的。

s1转让给s2,就像下图所示。s2拥有堆内存,s1变无效。
在这里插入图片描述

Rust语言中转让所有权的方式有以下几种:
1.把一个变量赋值给另一个变量。
2.把变量值传递给函数作为参数。
3.函数中返回一个变量作为返回值。

1.把一个变量赋值给另一个变量

fn main(){let a = Box::new(5i32);// a 是一个指向堆分配的整数的指针let b = a;// 移动a到b,把a的指针地址(而非数据)复制到b。现在是b拥有堆内存,a变无效。//println!("a contains: {}", a);// 报错!a无效,因为它不再拥有那部分堆上的内存。
}

2.把变量值传递给函数作为参数。
值传递方式,值的所有权也会发生变更

fn destroy_box(c: Box<i32>) {println!("Destroying a box that contains {}", c);// c 被销毁且内存得到释放
}
fn main() {let a = Box::new(5i32);// a 是一个指向堆分配的整数的指针destroy_box(a);// a的所有权转移给函数形参,a变无效//println!("a contains: {}", a);// 报错!a无效
}

3.函数中返回一个变量作为返回值
函数的形参获得的所有权将在离开函数后就失效了。失效的数据就再也访问不到了。
为了解决所有权失效的问题,我们可以让函数将所有者返回给调用者。

fn destroy_box(c: Box<i32>) ->Box<i32> {println!("Destroying a box that contains {}", c);c
}
fn main() {let mut a = Box::new(5i32);// a 是一个指向堆分配的整数的指针a = destroy_box(a);// a的所有权转移给函数形参,a变无效println!("b contains: {}", a);// 报错!a无效
}

(三)复刻
复刻,英文是clone。也叫深复制或深拷贝。
有时候,我们需要创建一个值的完全独立的副本,而不是转让所有权。在这种情况下,可以使用复刻。

创建s1的副本s2,就像下图所示。s1和s2都拥有了独立的所有权。
在这里插入图片描述

示例:

fn main() {let s1 = String::from("hello");let s2 = s1.clone();println!("{} {}", s1, s2); // 正常打印 "hello hello"
}

创建了字符串"hello"的副本,赋值给s2,因此s1和s2都拥有了独立的所有权。

fn main() {let a = Box::new(5i32);let b = a.clone();println!("{}", a);println!("{}", b);
}

栈上的数据
看下例

fn main() {let a = 50;let b = a;println!("{}", a);println!("{}", b);
}

这段代码能编译通过。按照所有权转让规则的话,它应该编译错误才对,可是为什么能编译通过?
因为像整型这样的类型完全存储在栈上,并不需要占用那么大的内存,所以拷贝它的值是很快的。没有理由在创建变量b后使a无效。这里没有深浅拷贝的区别,所以这里不管是否调用clone,效果都一样。
Rust有一个Copy trait,可以用于存储在栈上的类型。如果一个类型实现了Copy trait,那么就不使用所有权转让,而是使用复刻。

那么哪些类型实现了Copy trait呢?
Rust不允许自身或其部分实现了Drop trait的类型使用Copy trait。
任何简单标量值的组合都可以实现Copy,任何不在堆上分配内存的类型都可以实现Copy。
如下是一些Copy的类型:
1.布尔类型,bool
2.数字类型,包括整数和浮点数,比如 u32,f64。
3.字符类型,char
4.元组,当且仅当,其包含的类型都实现Copy的时候。比如,(i32, i32) 实现了Copy,但 (i32, String) 就没有。

相关文章:

rust所有权

一、堆和栈 栈和堆都是程序运行时使用的内存&#xff0c;但是它们的结构不同。 1.栈 栈&#xff0c;英文是stack。是内存的一段区域。 栈是后进先出形式的。就像薯片桶&#xff0c;先放进去的一片只能后拿出来。 栈上存储的数据大小必须是已知且固定的。也就是说如果一个变量…...

Win10电脑任务栏没有蓝牙图标的简单解决方法

Win10电脑任务栏没有蓝牙图标怎么办&#xff1f;在Win10电脑中&#xff0c;用户有时候会发现任务栏上没有蓝牙图标了&#xff0c;这样就无法通过蓝牙图标快速打开蓝牙服务了。下面小编给大家介绍最简单的解决方法&#xff0c;帮助大家找回任务栏上面的蓝牙图标吧。 问题原因 反…...

判断编译器类型、编译器版本、操作系统。

目录 1. 判断编译器类型&#xff1a; 2. 判断编译器版本&#xff1a; 3. 判断操作系统&#xff1a; 总结&#xff1a; 1. 判断编译器类型&#xff1a; 可以使用预定义的宏来判断编译器类型。例如&#xff0c;__GNUC__ 宏用于判断是否使用了GCC 编译器&#xff0c;_MSC_VER…...

百度实习一面(知识图谱部门)

百度面经&#xff08;知识图谱部&#xff09;一面 1.自我介绍 介绍完了&#xff0c;打开共享&#xff0c;对着简历一点一点问 2.ffmpeg在项目中是怎么使用的 回答了ffmpeg在项目中使用的命令&#xff0c;用来干了什么 3.为什么使用toml配置&#xff0c;了解过yml配置吗&am…...

Oracle 数据库查询优化

目录 1. Oracle 数据库查询优化(上百万级记录如何提高查询速度)2. Oracle SQL 性能优化 40 条 | 收藏了! 1. Oracle 数据库查询优化(上百万级记录如何提高查询速度) 对查询进行优化, 应尽量避免全表扫描, 首先应考虑在 where 及 order by 涉及的列上建立索引应尽量避免在 wher…...

时序预测 | MATLAB实现POA-CNN-GRU鹈鹕算法优化卷积门控循环单元时间序列预测

时序预测 | MATLAB实现POA-CNN-GRU鹈鹕算法优化卷积门控循环单元时间序列预测 目录 时序预测 | MATLAB实现POA-CNN-GRU鹈鹕算法优化卷积门控循环单元时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 MATLAB实现POA-CNN-GRU鹈鹕算法优化卷积门控循环单元时间序…...

Java技术接单

今天给大家介绍一个阶段性&#xff08;周期性&#xff09;能获取一定收益的Java技术接单群&#xff0c;分享给大家&#xff01;主要对搞Java的粉丝有帮助&#xff0c;因为可以赚点小钱&#xff0c;对Java技术的要求不高&#xff01; 注意&#xff1a;首先进群不是免费的&#…...

多家企业发布基于大模型的AI产品,大模型应用落地哪家强?

https://m.mp.oeeee.com/a/BAAFRD000020230603805161.html “无产业不AI&#xff0c;无应用不AI。” 随着AI&#xff08;人工智能&#xff09;大模型技术落地&#xff0c;AI应用遍地开花。连日来&#xff0c;多家企业发布基于大模型的AI应用产品。身处“百模大战”时代&#x…...

如何在小程序中获取用户昵称、电话号,头像

一、如何获取昵称&#xff08;获取微信昵称&#xff09;以Taro框架为例 Taro框架中的组件Input的一个属性&#xff0c;type属性的值有一个nickname. 如果要拿到input的值&#xff0c;是要value结合onChange事件。 type"nickname" value{nickName} onChange{(value: …...

26606-2011 工业用氰乙酸甲酯 阅读笔记

声明 本文是学习GB-T 26606-2011 工业用氰乙酸甲酯. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本标准规定了工业用氰乙酸甲酯的要求、试验方法、检验规则、标志、包装、运输、贮存和安全。 本标准适用于以氯乙酸、氰化钠、甲醇等为原料…...

微软开源 windows-drivers-rs, 用 Rust 开发 Windows 驱动程序

目录 1. 微软开源 windows-drivers-rs, 用 Rust 开发 Windows 驱动程序 1. 微软开源 windows-drivers-rs, 用 Rust 开发 Windows 驱动程序 Microsoft Azure 首席技术官兼著名 Windows 软件开发人员 Mark Russinovich 在社交平台上宣布, 启动了一个名为 windows-drivers-rs 的新…...

Java中判断字符串是否为合法数字

问题 最近遇到需要将String转BigDecimal的场景。 解决思路 利用NumberUtils.isCreatable判断是否为合法数字&#xff0c;然后&#xff0c;对字符串进行数字转换。注意&#xff1a;这里的NumberUtils类是org.apache.commons.lang3.math库里面的类。 Java if (NumberUtils.i…...

[LeetCode] Hard-2251. 花期内花的数目 - 二分查找/有序数组

Problem: 2251. 花期内花的数目 2251. 花期内花的数目 思路解题方法Code 思路 看题目应该是一道比较经典的差分&#xff0c;本来准备拿差分数组做的&#xff0c;后来搂了一眼题解&#xff0c;发现用二分的方法更简单 解题方法 此题有一种很简便的方法&#xff0c;第i个人到…...

VUE3父子组件传值defineProps() 和 defineEmits()

defineProps 和 defineEmits 都是只能在<script setup>中使用的编译器宏。他们不需要导入&#xff0c;且会随着 <script setup> 的处理过程一同被编译掉。 官网传送门 父组件向子组件传值 defineProps 是 Vue3 中一种新的组件数据传递方式&#xff0c;可以用于在…...

OmniPlan Pro 4 for Mac:引领项目管理的创新与高效

OmniPlan Pro 4是一款强大且高效的项目管理工具&#xff0c;专为Mac用户设计。它提供了一套综合性的解决方案&#xff0c;帮助用户在Mac上便捷地进行项目规划、追踪和管理。凭借其直观的界面&#xff0c;用户可以快速上手&#xff0c;并且能充分利用这款工具的各种功能。 规划…...

封装JDBC,实现简单ORM框架

本文将封装JDBC的操作&#xff0c;实现简单的ORM框架&#xff0c;提供3种风格的api来给用户使用&#xff08;1.原生jdbcSqlBuilder&#xff1b;2.类似jpa和mp的&#xff1b;3.注解接口方法&#xff09; 代码仓库&#xff1a;malred/IFullORM 1. 原生JDBCsql构建器 第一步&…...

监控与运维,主流it运维监控工具

IT监管和运行维护已成为企业经营的关键环节。本文将详细介绍IT监管和运行维护的必要性、主要功能和实施策略&#xff0c;帮助企业实现数据安全和高效运行。 IT监管和运行维护的必要性 确保企业数据安全 IT监控系统可以实时监控企业网络、服务器、存储等关键设备的运行情况&…...

基于Matlab实现全局优化算法

Matlab是一种非常强大的数学建模和计算工具&#xff0c;它提供了许多优化算法的实现。全局优化算法是一种能够找到全局最优解的优化算法&#xff0c;相对于局部优化算法来说&#xff0c;具有更强的全局搜索能力。在本文中&#xff0c;我们将介绍如何使用Matlab实现全局优化算法…...

Kafka 笔记 (Non-Root/Container)

目录 1. Kafka 笔记 (Non-Root/Container)1.1. 启动1.2. bitnami/kafka1.2.1. Non-Root Containers 1. Kafka 笔记 (Non-Root/Container) 1.1. 启动 Kafka 需要与 ZooKeeper 一起启动: Kafka with ZooKeeper Run the following commands in order to start all services in…...

【Pytest】跳过执行之@pytest.mark.skip()详解

一、skip介绍及运用 在我们自动化测试过程中&#xff0c;经常会遇到功能阻塞、功能未实现、环境等一系列外部因素问题导致的一些用例执行不了&#xff0c;这时我们就可以用到跳过skip用例&#xff0c;如果我们注释掉或删除掉&#xff0c;后面还要进行恢复操作。 1、skip跳过成…...

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…...

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;供调用如何按…...

Objective-C常用命名规范总结

【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名&#xff08;Class Name)2.协议名&#xff08;Protocol Name)3.方法名&#xff08;Method Name)4.属性名&#xff08;Property Name&#xff09;5.局部变量/实例变量&#xff08;Local / Instance Variables&…...

C++中string流知识详解和示例

一、概览与类体系 C 提供三种基于内存字符串的流&#xff0c;定义在 <sstream> 中&#xff1a; std::istringstream&#xff1a;输入流&#xff0c;从已有字符串中读取并解析。std::ostringstream&#xff1a;输出流&#xff0c;向内部缓冲区写入内容&#xff0c;最终取…...

AI书签管理工具开发全记录(十九):嵌入资源处理

1.前言 &#x1f4dd; 在上一篇文章中&#xff0c;我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源&#xff0c;方便后续将资源打包到一个可执行文件中。 2.embed介绍 &#x1f3af; Go 1.16 引入了革命性的 embed 包&#xff0c;彻底改变了静态资源管理的…...

如何在网页里填写 PDF 表格?

有时候&#xff0c;你可能希望用户能在你的网站上填写 PDF 表单。然而&#xff0c;这件事并不简单&#xff0c;因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件&#xff0c;但原生并不支持编辑或填写它们。更糟的是&#xff0c;如果你想收集表单数据&#xff…...

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

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

springboot整合VUE之在线教育管理系统简介

可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生&#xff0c;小白用户&#xff0c;想学习知识的 有点基础&#xff0c;想要通过项…...

Selenium常用函数介绍

目录 一&#xff0c;元素定位 1.1 cssSeector 1.2 xpath 二&#xff0c;操作测试对象 三&#xff0c;窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四&#xff0c;弹窗 五&#xff0c;等待 六&#xff0c;导航 七&#xff0c;文件上传 …...

Linux nano命令的基本使用

参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时&#xff0c;显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...