Rust入门-所有权

一、为什么、是什么、怎么用

1、为什么Rust要提出一个所有权和借用的概念

所有的程序都必须和计算机内存打交道,如何从内存中申请空间来存放程序的运行内容,如何在不需要的时候释放这些空间,成为所有编程语言设计的难点之一。

主要分为三种流派:

  1. (1)垃圾回收机制(GC),在程序运行时不断寻找不再使用的内存,典型代表:Java、Go

    (2)手动管理内存的分配和释放, 在程序中,通过函数调用的方式来申请和释放内存,典型代表:C++

    (3)通过所有权来管理内存,编译器在编译时会根据一系列规则进行检查避免手动或者运行时垃圾回收带来的额外成本

讲到内存,在编程语言中,都会讲到栈、堆,栈和堆的结构特性决定了一些值适合放在哪些位置,能够有更好的性能和空间效率。

  1. (1)栈,主要是存储局部变量,栈中的所有数据都必须占用已知且固定大小的内存空间。

    优点:用完即出,也很好出,性能很好 缺点:无法存储大小未知或者可能变化的数据。

    (2)堆,对于大小未知或者可能变化的数据,我们需要将它存储在堆上。

    优点:空间大,能够存储大小未知或者可能变化的数据。 缺点:数据组织较为复杂,需要回收内存空间。

2、Rust所有权是什么

先来介绍所有权的几个概念

  1. Rust 中每一个值被一个变量所拥有,该变量被称为值的所有者,且有且仅有一个所有者

    所有者(变量)离开作用域范围时,这个值将被丢弃(drop)

特别地,为了便于理解,我认为基本数据类型值**,比如i32、boolean等基本类型,有别的机制处理这种基本类型的所有权问题:会直接拷贝栈上数据,新生成一个值,把新值的所有权给新变量,不会把旧值的所有权给新变量(新拥有者),没有发生所有权变化的现象。这种叫做Copy行为。

先说一下字符串类型,注意我说的是字符串类型,对于

let s = "hello";

上面这段代码中的“hello”,可以理解成Java中的字符串字面值不是存储在堆上的,可以想象成存储在一个文件里的。和字符串类型不是一个东西,对于这种值,可以理解成没有所有权的概念,大家都只是持有一个引用它的指针。

那么 什么是字符串类型的数据,比如

let s1 = String::from("hello");
let s2 = s1;

我们来分析上述代码的

第一行代码 String::from(“hello”);

  1. 会在堆中找到一片地址空间,存储字符串类型数据"hello"。并返回该堆中数据的地址、长度、容量等数据,此时堆中的数据就叫做值

    然后在栈中生成一个结构体变量s1,该结构体就是字符串结构,保存了堆中数据的地址、长度、容量等数据。s1变量堆中数据(值)的拥有者

第二行代码 let s2 = s1;

这种行为,就是将s1的值的所有权移交给了s2,即堆中数据"hello"此时的拥有者是变量上s2s1已经没有"hello"的所有权,我们不能再通过s1访问或者修改堆中数据"hello"。

如果此时想通过s1再次访问堆中数据"hello",就会报错


fn main() {
  let s1 = String::from("hello");
  let s2 = s1;
  
  println!("{}, world!", s1);
  
}
error[E0382]: borrow of moved value: `s1`
 --> src/main.rs:6:26
  |
3 |   let s1 = String::from("hello");
  |       -- move occurs because `s1` has type `String`, which does not implement the `Copy` trait
4 |   let s2 = s1;
  |            -- value moved here
5 |   
6 |   println!("{}, world!", s1);
  |                          ^^ value borrowed here after move
  |
  = 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

再来说一下这样移交所有权有什么好处。

  1. 当变量离开作用域后,Rust 会自动调用 drop 函数并清理变量的堆内存。不过由于两个 String
    变量指向了同一位置。这就有了一个问题:当 s1 和 s2 离开作用域,它们都会尝试释放相同的内存。这是一个叫做 二次释放(double free) 的错误,也是之前提到过的内存安全性 BUG 之一。两次释放(相同)内存会导致内存污染,它可能会导致潜在的安全漏洞

可以理解成,所有权移交之后,就可以大胆放心的抛弃先前的拥有者。来个图加深一下印象
请添加图片描述

s1不再指向堆中数据,s2指向堆中数据

3、我就不想移交所有权,我又想生成一个新变量指向相同数据

这里可以用到深拷贝,即在堆中生成一份相同的数据,赋给新变量。如代码


fn main() {
  let s1 = String::from("hello");
  let s2 = s1.clone();
  
  println!("{}, world!", s1);
  
}

但是这么做是有性能消耗的,因为你需要复制一份数据,万一你这个数据非常大,复制起来非常耗时耗资源

而对于栈上变量,直接都是深拷贝,其实不是叫深拷贝,是达到深拷贝的效果,但是Rust叫做Copy

这里可以给出一个通用的规则: 任何基本类型的组合可以 Copy ,不需要分配内存或某种形式资源的类型是可以 Copy 的。如下是一些 Copy 的类型:

  1. 所有整数类型,比如 u32
  2. 布尔类型,bool,它的值是 true 和 false
  3. 所有浮点数类型,比如 f64 字符类型,char
  4. 元组,当且仅当其包含的类型也都是 Copy 的时候。比如,(i32, i32) 是 Copy 的,但 (i32, String) 就不是
  5. 不可变引用 &T ,

可变引用 &mut T 是不可以 Copy的

3、函数传参和返回值,都是会移交所有权的

fn main() {
    let s = String::from("hello");  // s 进入作用域

    takes_ownership(s);             // s 的值移动到函数里 ...
                                    // ... 所以到这里不再有效

    let x = 5;                      // x 进入作用域

    makes_copy(x);                  // x 应该移动函数里,
                                    // 但 i32 是 Copy 的,所以在后面可继续使用 x

} // 这里, x 先移出了作用域,然后是 s。但因为 s 的值已被移走,
  // 所以不会有特殊操作

fn takes_ownership(some_string: String) { // some_string 进入作用域
    println!("{}", some_string);
} // 这里,some_string 移出作用域并调用 `drop` 方法。占用的内存被释放

fn makes_copy(some_integer: i32) { // some_integer 进入作用域
    println!("{}", some_integer);
} // 这里,some_integer 移出作用域。不会有特殊操作

你可以尝试在 takes_ownership 之后,再使用 s,看看如何报错?例如添加一行 println!(“在move进函数后继续使用s: {}”,s);。

有时我就想用一下,老是移来移去多麻烦,下篇我们讲引用与借用

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MemFire解决方案-政企数据库云服务解决方案

方案背景 随着越来越多的政府部门/企事业单位完成数字化转型升级,新技术的应用日益普遍,对系统并发服务能力的需求不断提高。办公OA、档案、门户、监控、财务、ERP、订单等各类系统对数据库的要求越来越苛刻,很多企业/政府部门都面临如下挑战…

Unity的Animator Animation的使用攻略

Animator 动画控制器 Animation 动画 动画片段 .anin 一、创建Animator 创建动画控制器 模型添加Animator组件 把控制器和模型绑定 二、创建动画 进入动画界面 创建动画片段anin 动画窗口分析 制作动画 点击录制, 移动子对象,在视窗 通过移动线来编辑关…

第53篇:算法的硬件实现<四>

Q:本期我们在DE2-115开发板上实现二进制搜索算法电路,查找数据A在数组中的位置。 A:使用SW[9]设定开始查找信号,数据A由SW[7:0]设定,KEY[0]设定为复位信号,板载50MHz时钟作为电路的时钟输入,确…

Python | Leetcode Python题解之第44题通配符匹配

题目: 题解: class Solution:def isMatch(self, s: str, p: str) -> bool:def allStars(st: str, left: int, right: int) -> bool:return all(st[i] * for i in range(left, right))def charMatch(u: str, v: str) -> bool:return u v or v…

1125页Go语言技术手册,涵盖Go语言所有核心知识点,限时免费下载!

从Docker的兴起,到Kubernetes的冲击,让Go语言在后端的地位,尤其在偏中高级业务需求(对性能、代码质量、架构设计等)中已经不可撼动。后端开发工程师逐渐开始对Go语言产生兴趣,无论是擅长何种语言的后端工程师,个人认为…

德思特车载天线方案:打造智能互联的公共安全交通网络

作者介绍 一、方案介绍 随着自动驾驶与智慧汽车概念的逐步推进,人们对汽车的交互性、智能性、互联性有了更高的要求。今天,大多数汽车制造商和供应商普遍将GNSS定位功能与其他信号如广播、电视、蓝牙、Wifi一起集成到汽车中,包括博世、大陆、…

泽攸科普——扫描电子显微镜(SEM)全攻略:轻轻松松搞定形貌特征

在科学的殿堂里,有一台神奇的仪器,它能将我们的视野拓展到难以想象的微观尺度,让我们得以窥探那些肉眼无法捕捉的精妙结构和隐秘细节。这台仪器就是扫描电子显微镜(Scanning Electron Microscope, SEM)。它犹如一双洞察…

车灯专用方案12V24V36V48V转9V/12V 线性恒流H7306

电流控制:为了保持输出电流的恒定,转换器使用一种称为恒流控制的技术。恒流控制通常通过测量输出电流并与一个参考电流进行比较来实现。如果输出电流超过参考电流,控制电路将调整输出电压或电流,以降低输出电流至所需的恒定值。 …

2023年蓝桥杯C++A组第三题:更小的数(双指针暴力遍历解法)

题目描述 小蓝有一个长度均为 n 且仅由数字字符 0 ∼ 9 组成的字符串,下标从 0 到 n − 1,你可以将其视作是一个具有 n 位的十进制数字 num,小蓝可以从 num 中选出一段连续的子串并将子串进行反转,最多反转一次。小蓝想要将选出的…

云盘怎么选最好!

选择云盘时,您应该考虑以下几个关键因素来确保选中最适合您需求的服务: 速度:选择云盘时,传输速度是一个重要的考量点。您应该选择一个即使不使用会员服务也能提供较快上传和下载速度的云盘服务。 存储空间:不同的云盘…

Ardupilot OpenIPC 基于WFB-NG构架分析和数据链路思考

Ardupilot & OpenIPC & 基于WFB-NG构架分析和数据链路思考 1. 源由2. OpenIPC安装2.1 安装2.2 配置2.2.1 天空端配置文件2.2.2 地面端配置文件 2.3 当前配置选择 3. WFB-NG安装3.1 RTL8812AU安装3.1.1 驱动安装3.1.2 定位设备 3.2 wfb-ng安装3.2.1 传输层安装3.2.2 配置…

WordPress social-warfare插件XSS和RCE漏洞【CVE-2019-9978】

WordPress social-warfare插件XSS和RCE漏洞 ~~ 漏洞编号 : CVE-2019-9978 影响版本 : WordPress social-warfare < 3.5.3 漏洞描述 : WordPress是一套使用PHP语言开发的博客平台&#xff0c;该平台支持在PHP和MySQL的服务器上架设个人博客网站。social-warfare plugin是使用…

Kimi Chat四大更新详细解读!模型能力提升,支持语音输入输出!

大家好&#xff0c;我是木易&#xff0c;一个持续关注AI领域的互联网技术产品经理&#xff0c;国内Top2本科&#xff0c;美国Top10 CS研究生&#xff0c;MBA。我坚信AI是普通人变强的“外挂”&#xff0c;所以创建了“AI信息Gap”这个公众号&#xff0c;专注于分享AI全维度知识…

clickhouse数据去重函数介绍(count distinct)

非精确去重函数&#xff1a;uniq、uniqHLL12、uniqCombined 精确去重函数&#xff1a;uniqExact、groupBitmap 测试数据量&#xff1a;2000w 结论&#xff1a; 1.整形值精确去重场景&#xff0c;groupBitmap 比 uniqExact至少快 2x 2.groupBitmap仅支持无符号整形值去重&#x…

【LAMMPS学习】八、基础知识(3.6)计算热导率

8. 基础知识 此部分描述了如何使用 LAMMPS 为用户和开发人员执行各种任务。术语表页面还列出了 MD 术语&#xff0c;以及相应 LAMMPS 手册页的链接。 LAMMPS 源代码分发的 examples 目录中包含的示例输入脚本以及示例脚本页面上突出显示的示例输入脚本还展示了如何设置和运行各…

我的一些 35+ 前同事的现状

大家好&#xff0c;我是坤哥&#xff0c;好久不见&#xff0c;今天简单和大家聊一下我目前观察到的前同事的现状 今年和一些前同事简单聊过&#xff0c;他们的现状如下&#xff1a; A: 去新西兰做 iOS 开发快 10 年了&#xff0c;马上就要拿到永久居留证了&#xff0c;他说在新…

hcip实验 — 路由策略实验

目录 实验拓扑 实验要求 实验思路 实验步骤 1.配置接口及环回ip 2.配置ospf协议及rip协议 3.在R2上进行路由引入 4.在R2上进行路由过滤 5.在R4上进行路由过滤&#xff08;地址前缀列表&#xff09; 6.在R2 RIP进程上配置静默接口使RIP报文无法进入OSPF区域 实验拓扑 …

Hadoop实战——MapReduce-字符统计(超详细教学,算法分析)

目录 一、前提准备工作 启动hadoop集群 二、实验过程 1.虚拟机安装先设置端口转发 2.上传对应文件 3.编写Java应用程序 4. 编译打包程序 5. 运行程序 三、算法设计和分析 算法设计 算法分析 四、实验总结 实验目的&#xff1a;给定一份英文文本&#xff0c;统计每个…

ResNet详解

一、认识ResNet ResNet&#xff08;Residual Network&#xff09;是一种深度神经网络结构&#xff0c;被广泛应用于图像分类、目标检测和语义分割等计算机视觉任务中。它是由微软亚洲研究院的何凯明等人于2015年提出的&#xff0c;通过引入残差连接&#xff08;residual conne…

携程 Java 暑期实习一面:HashMap 的 key 可以设置为 null 吗?那 ConcurrentHashMap 呢?

更多大厂面试内容可见 -> http://11come.cn 携程 Java 暑期实习一面&#xff1a;HashMap 的 key 可以设置为 null 吗&#xff1f;那 ConcurrentHashMap 呢&#xff1f; Java 基础 1、Java 中有哪些常见的数据结构&#xff1f; 图片来源于&#xff1a;JavaGuide Java 中常…
最新文章