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/news/827320.shtml

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

相关文章

git merge 和 git rebese的区别

git merge 和 git rebese的区别 拉取分支和合并代码会涉及两种选择,git merge 和 git rebase: rebase:变基,会有一个干净的分支,但是对于记录来源不够清楚merge:合并,git 分支看起来比较混乱&…

CentOS 7虚拟机配置静态IP地址(一)

IP地址的配置 以下几个地址需要记住,在配置中使用 (1)查看MAC地址(点击菜单虚拟机-设置-网络适配器-高级-记住MAC地址) (2)查看子网掩码和网关IP(点击菜单编辑-虚拟网络编辑器-选择…

机器学习-10-神经网络python实现-从零开始

文章目录 总结参考本门课程的目标机器学习定义从零构建神经网络手写数据集MNIST介绍代码读取数据集MNIST神经网络实现测试手写的图片 带有反向查询的神经网络实现 总结 本系列是机器学习课程的系列课程,主要介绍基于python实现神经网络。 参考 BP神经网络及pytho…

Reactor 模式

目录 1. 实现代码 2. Reactor 模式 3. 分析服务器的实现具体细节 3.1. Connection 结构 3.2. 服务器的成员属性 3.2. 服务器的构造 3.3. 事件轮询 3.4. 事件派发 3.5. 连接事件 3.6. 读事件 3.7. 写事件 3.8. 异常事件 4. 服务器上层的处理 5. Reactor 总结 1…

公钥密码学Public-Key Cryptography

公钥或非对称密码学的发展是整个密码学历史上最伟大的,也许是唯一真正的革命。The development of public-key, or asymmetric, cryptography is the greatest and perhaps the only true revolution in the entire history of cryptography. 公钥算法基于数学函数…

node.js如何实现留言板功能?

一、实现效果如下: 20240422_160404 二、前提配置: 配置:需要安装并且导入underscore模板引擎 安装:在控制台输入npm install underscore -save 文件目录配置: 1》在文件里建一个data文件夹,此文件夹下…

ContextMenuStrip内容菜单源对象赋值学习笔记(含源码)

一、前言 MetroTileItem属于第三方控件,无法定义ContextMenuStrip属性 想实现某子项点击菜单时,与源控件(按钮metroTileItem)的某值对应,用于动态控制按钮的状态或方法 1.1 效果 二、实现方法 2.1 方法1 (代码,说明见注释) private void metroTileItem_MouseDown(o…

【题解】AB5 点击消除(栈)

https://www.nowcoder.com/practice/8d3643ec29654cf8908b5cf3a0479fd5?tpId308&tqId40462&ru/exam/oj 把string当栈用&#xff0c;扫一遍就可以了&#xff0c;时间复杂度O(n) #include <iostream> #include <string> using namespace std;int main() {…

向量的点积和叉积的几何意义

1. 点积 点积(dot product)&#xff0c;又称标量积&#xff08;scalar product&#xff09;。结果等于。 可用于 判断的是否垂直求投影长度求向量是抑制作用还是促进作用 2. 叉积 叉积(cross product)&#xff0c;又称为向量积(vector product)。模长等于&#xff0c;方向…

Golang | Leetcode Golang题解之第43题字符串相乘

题目&#xff1a; 题解&#xff1a; func multiply(num1 string, num2 string) string {if num1 "0" || num2 "0" {return "0"}m, n : len(num1), len(num2)ansArr : make([]int, m n)for i : m - 1; i > 0; i-- {x : int(num1[i]) - 0fo…

详细说说,中介怎么做!CLHLS数据库探索抑郁症状的中介作用发文二区

零基础CHARLS发论文&#xff0c;不容错过&#xff01; 长期回放更新指导&#xff01;适合零基础&#xff0c;毕业论文&#xff0c;赠送2011-2020年CHARLS清洗后的数据全套代码&#xff01; 2024年3月28日&#xff0c;中国学者用CLHLS数据库最新数据&#xff08;2018年&#xff…

java-Arrays

一、Arrays的概述 Arrays是操作数组的工具类 二、Arrays的常用方法 Arrays的常用方法基本上都被static静态修饰&#xff0c;因此在使用这些方法时&#xff0c;可以直接通过类名调用 1.toString 语法&#xff1a;Arrays.toString(数组) 用于将数组的元素转换为一个字符串&a…

蓝桥杯第17169题——兽之泪II

问题描述 在蓝桥王国&#xff0c;流传着一个古老的传说&#xff1a;在怪兽谷&#xff0c;有一笔由神圣骑士留下的宝藏。 小蓝是一位年轻而勇敢的冒险家&#xff0c;他决定去寻找宝藏。根据远古卷轴的提示&#xff0c;如果要找到宝藏&#xff0c;那么需要集齐 n 滴兽之泪&#…

Git | 分支管理

Git | 分支管理 文章目录 Git | 分支管理1、理解分支2、创建分支&&切换分支3、合并分支4、删除分支5、合并冲突6、分支管理策略合并分支模式实际工作中分支策略bug分支删除临时分支 1、理解分支 分支就类似分身。 在版本回退中&#xff0c;每次提交Git都会将修改以git…

简单学量化——pandas的应用26——sort_values函数5

简单学量化——pandas的应用26——sort_values函数5 sort_values是pandas中的排序函数&#xff0c;语法如下&#xff1a; DataFrame.sort_values(by,axis0,ascendingTrue,inplaceFalse,kindquicksort,na_positionlast, ignore_indexFalse,keyNone) 前面我们学习了by、axis、a…

C++之写时复制(CopyOnWrite)

设计模式专栏&#xff1a;http://t.csdnimg.cn/4j9Cq 目录 1.简介 2.实现原理 3.QString的实现分析 3.1.内部结构 3.2.写入时复制 4.示例分析 5.使用场景 6.总结 1.简介 CopyOnWrite (COW) 是一种编程思想&#xff0c;用于优化内存使用和提高性能。COW 的基本思想是&am…

go的编译以及运行时环境

开篇 很多语言都有自己的运行时环境&#xff0c;go自然也不例外&#xff0c;那么今天我们就来讲讲go语言的运行时环境&#xff01; 不同语言的运行时环境对比 我们都知道Java的运行时环境是jvm &#xff0c;javascript的运行时环境是浏览器内核 Java -->jvm javascript…

FastWiki一分钟本地离线部署本地企业级人工智能客服

介绍 FastWiki是一个开源的企业级人工智能客服系统&#xff0c;它使用了一系列先进的技术和框架来支持其功能。 技术栈 前端框架&#xff1a;React LobeUI TypeScript后端框架&#xff1a;MasaFramework 基于 .NET 8动态函数&#xff1a;基于JavaScript V8引擎实现向量搜索…

物联网配网工具多元化助力腾飞——智能连接,畅享未来

随着物联网技术的迅猛发展&#xff0c;智能插座、蓝牙网关作为其中常见的智能物联设备&#xff0c;无论是功能还是外观都有很大的改进&#xff0c;在智能化越来越普遍的情况下&#xff0c;它们的应用场景也在不断拓宽。对于智能设备而言&#xff0c;配网方式的选择对于设备的成…

Jenkins CI/CD 持续集成专题一 Jenkins的安装和配置

一 jenkins 官方教程 安装Jenkins 二 安装 2.1 安装方式一 通过安装包的package方式安装 第一步下载链接&#xff1a; Download the latest package 第二步操作方式&#xff1a;打开包并按照说明操作即可安装 2.2 安装方式二 brew安装 第一 安装最新版本jenkins brew in…