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.mzph.cn/bicheng/289.shtml

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

相关文章

java新冠病毒密接者跟踪系统(springboot+mysql源码+文档)

风定落花生,歌声逐流水,大家好我是风歌,混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的新冠病毒密接者跟踪系统。项目源码以及部署相关请联系风歌,文末附上联系信息 。 项目简介: 新冠病毒密接者跟…

Java垃圾回收1

1.对象什么时候可以被垃圾器回收 1.垃圾回收的概念 为了让程序员更专注于代码的实现,而不用过多的考虑内存释放的问题,所以, 在Java语言中,有了自动的垃圾回收机制,也就是我们熟悉的GC(Garbage Collection)。 有了垃圾…

2、MATLAB入门常用命令

一、退出和中断 exit和quit:结束MATLAB会话。程序完成,如果没有明确保存,则变量中的数据丢失。 Ctrl c:中断一个MATLAB任务。例如,当MATLAB正在计算或打印时,中断一个任务,但会话并没有结束。…

麒麟服务器操作系统自动化安装应答文件制作

原文链接:麒麟服务器操作系统自动化安装应答文件制作 Hello,大家好啊!今天我们将探讨如何为麒麟服务器操作系统制作自动化安装应答文件。在部署大量服务器时,自动化安装是提高效率和确保安装一致性的关键技术。通过使用应答文件&a…

云原生Kubernetes: K8S 1.29版本 部署Kuboard

目录 一、实验 1.环境 2.K8S 1.29版本 部署Kuboard (第一种方式) 3.K8S 1.29版本 部署Kuboard (第二种方式) 4.K8S 1.29版本 使用Kuboard 二、问题 1.docker如何在node节点间移动镜像 一、实验 1.环境 (1)主机 表1 主机 主机架构版本IP备注ma…

太阳能路灯光伏板的朝向设计问题

题目:太阳能路灯光伏板的朝向设计问题 难度对标几乎每一年的国赛A题。 QQ群:592697532 公众号:川川菜鸟 文章目录 背景问题问题一问题二问题三 题目解读相关公式(必备)太阳辐射的计算光伏板接收的辐射光学效率大 气透…

Spring Cloud Gateway详细介绍以及实现动态路由

一. 简介 Spring Cloud Gateway This project provides a libraries for building an API Gateway on top of Spring WebFlux or Spring WebMVC. Spring Cloud Gateway aims to provide a simple, yet effective way to route to APIs and provide cross cutting concerns to …

C++的线程

#include<iostream> #include<thread> #include<unistd.h> using namespace std; void myrun() {while(true){cout<<"I am a thread"<<endl;sleep(1);} } int main() {thread t(myrun);t.join();return 0; } 如果不添加-lpthread就会报…

基于ChatGPT打造安全脚本工具流程

前言 以前想要打造一款自己的工具&#xff0c;想法挺好实际上是难以实现&#xff0c;第一不懂代码的构造&#xff0c;只有一些工具脚本构造思路&#xff0c;第二总是像重复造轮子这种繁琐枯燥工作&#xff0c;抄抄改改搞不清楚逻辑&#xff0c;想打造一款符合自己工作的自定义的…

Day 25 组合(优化)216.组合总和III 17.电话号码的字母组合

组合&#xff08;优化&#xff09; 先给出组合问题的回溯部分代码&#xff1a; vector<vector<int>> result; // 存放符合条件结果的集合vector<int> path; // 用来存放符合条件结果void backtracking(int n, int k, int startIndex) {if (path.size() k) …

【opencv】dnn示例-person_reid.cpp 人员识别(ReID,Re-Identification)系统

ReID(Re-Identification&#xff0c;即对摄像机视野外的人进行再识别) 0030_c1_f0056923.jpg 0042_c5_f0068994.jpg 0056_c8_f0017063.jpg 以上为输出结果&#xff1a;result文件夹下 galleryLIst.txt queryList.txt 模型下载&#xff1a; https://github.com/ReID-Team/ReID_e…

OpenHarmony网络通信-socket-io

简介 socket.io是一个在客户端和服务器之间实现低延迟、双向和基于事件的通信的库。建立在 WebSocket 协议之上&#xff0c;并提供额外的保证&#xff0c;例如回退到 HTTP 长轮询或自动重新连接。 效果展示 下载安装 ohpm install ohos/socketio OpenHarmony ohpm 环境配置等更…

VulnHub靶机 DC-5 打靶 渗透测试详情过程

VulnHub靶机 DC-5 打靶 详细渗透测试过程 目录 VulnHub靶机 DC-5 打靶 详细渗透测试过程一、将靶机导入到虚拟机当中二、渗透流程主机发现端口扫描目录爆破文件包含getshell反弹shell提权 一、将靶机导入到虚拟机当中 靶机地址&#xff1a; https://download.vulnhub.com/dc/…

【云计算】云计算八股与云开发核心技术(虚拟化、分布式、容器化)

【云计算】云计算八股与云开发核心技术&#xff08;虚拟化、分布式、容器化&#xff09; 文章目录 一、什么是云计算&#xff1f;1、云计算的架构&#xff08;基础设施&#xff0c;平台&#xff0c;软件&#xff09;2、云计算的发展 二、如何做云计算开发&#xff1f;云计算的核…

量子时代加密安全与区块链应用的未来

量子时代加密安全与区块链应用的未来 现代密码学仍然是一门相对年轻的学科&#xff0c;但其历史却显示了一种重要的模式。大多数的发展都是基于几年甚至几十年前的研究。而这种缓慢的发展速度也是有原因的&#xff0c;就像药物和疫苗在进入市场之前需要经过多年的严格测试一样&…

心学从0开始学习rust-十万个为什么篇章(持续更新篇章)

问答环节 1.const x 1;和let x 1有何区别呢&#xff0c;const申请的是全局变量所以会一直有效对吗&#xff1f; const 声明的常量具有全局作用域&#xff0c;但它们不能直接在函数内部声明。常量通常用于定义整个程序中使用的值&#xff0c;如配置常量或数学常量。 let 声明…

tcp网络编程——2

1.一个服务器只能有一个客户端连接&#xff08;下面代码&#xff09; ​​​​​​​tcp网络编程&#xff08;基础&#xff09;-CSDN博客 2.一个服务器可以有多个客户端连接&#xff08;多线程&#xff09; server端创建多个线程&#xff0c;每个线程与不同的client端建立连…

浅写个登录(无js文件)

全部代码如下&#xff0c;无需编写wxss文件&#xff0c;渲染都在style里面&#xff1a; <view style"height: 250rpx;width: 100%;"> <!-- 背景图片 --><view style"position: absolute; background-color: antiquewhite; height: 250rpx;width…

IDEA中Docker相关操作的使用教程

一、引言 Docker作为当前最流行的容器化技术&#xff0c;极大地简化了应用的部署和管理。而IntelliJ IDEA作为一款强大的集成开发环境&#xff0c;也提供了对Docker的集成支持。本文将介绍如何在IDEA中配置和使用Docker&#xff0c;包括远程访问配置、服务连接、Dockerfile编写…

【C语言】冒泡排序算法详解

目录 一、算法原理二、算法分析时间复杂度空间复杂度稳定性 三、C语言实现四、Python实现 冒泡排序&#xff08;Bubble Sort&#xff09;是一种基础的排序算法。它重复地遍历要排序的数列&#xff0c;一次比较两个元素&#xff0c;如果他们的顺序错误就把他们交换过来。遍历数列…