Rust语言从入门到入坑——(5)Rust 所有权

news2024/11/16 23:59:03

文章目录

  • 0 引入
  • 1、所有权
  • 2、内存和分配
  • 3、移动与克隆
    • 3.1、移动
    • 3.2、克隆
  • 4、引用与租借
    • 4.1、引用
    • 4.1、垂悬引用
  • 5、函数中变量
    • 5.1 参数变量
    • 5.2 、返回值变量


在这里插入图片描述

0 引入

主要介绍Rust所有权的知识,涉及到变量的作用域,内存释放机制,移动,克隆,引用等知识,很多知识是Rust语言特有机制。


1、所有权

所有权有以下三条规则:

- Rust 中的每个值都有一个变量,称为其所有者。
- 一次只能有一个所有者。
- 当所有者不在程序运行范围时,该值将被删除。

{
    // 在声明以前,变量 s 无效
    let s = "runoob";
    // 这里是变量 s 的可用范围
}
// 变量范围已经结束,变量 s 无效

2、内存和分配

大多数的编程语言都有管理内存的功能:

1、C/C++ 这样的语言主要通过手动方式管理内存,开发者需要手动的申请和释放内存资源。但为了提高开发效率,只要不影响程序功能的实现,许多开发者没有及时释放内存的习惯。所以手动管理内存的方式常常造成资源浪费,而且容易出现内存释放遗忘,造成内存泄漏。
2、Java 语言编写的程序在虚拟机(JVM)中运行,JVM 具备自动回收内存资源的功能。但这种方式常常会降低运行时效率,所以 JVM 会尽可能少的回收资源,这样也会使程序占用较大的内存资源。
3、所有权对大多数开发者而言是一个新颖的概念,它是 Rust 语言为高效使用内存而设计的语法机制。所有权概念是为了让 Rust 在编译阶段更有效地分析内存资源的有用性以实现内存管理而诞生的概念。
4、所有权的设定,看起来很奇怪,其实本质上就是在语言层面禁止了同一个可变数据会有多个变量引用的情况,一旦作为参数传递了,就会发生所有权的移动(Move)或借用(Borrow)。赋值给另一个变更也就自动放弃了所有权。从根本上杜绝了并发情景下的数据共享冲突。
5、Rust数据类型可以分为基本的数据类型和其他可变数据类型(String等可变长)
基本数据"类型有这些:
所有整数类型,例如 i32 、 u32 、 i64 等。
布尔类型 bool,值为 true 或 false 。
所有浮点类型,f32 和 f64。
字符类型 char。
仅包含以上类型数据的元组(Tuples)。

基本数据一般定义的时候都在栈上,其他的在堆上,数据在哪上面,只要所有权丧失,Rust就会自动释放


3、移动与克隆

3.1、移动

如下代码:

    //基本数据在栈中定义,交互数据是直接复制的,通过移动复制
    let x = 5;
    let y = x;
    println!("{}, {}",x,y);

	//其他数据在堆中交互
	let s1 = String::from("hello");
	let s2 = s1;                // 为了确保安全,在给 s2 赋值时 s1 已经无效了
	println!("{}, world!", s1); // 错误!s1 已经失效

3.2、克隆

Rust会尽可能地降低程序的运行成本,所以默认情况下,长度较大的数据存放在堆中,且采用移动的方式进行数据交互。但如果需要将数据单纯的复制一份以供他用,可以使用数据的第二种交互方式——克隆。

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

这样就会有两份独立的数据。


4、引用与租借

4.1、引用

引用(Reference)是 C++ 开发者较为熟悉的概念。如果你熟悉指针的概念,你可以把它看作一种指针。实质上"引用"是变量的间接访问方式。

fn main() {
    let s1 = String::from("hello");
    let s2 = &s1;
    println!("s1 is {}, s2 is {}", s1, s2);
}

1、当一个变量的值被引用时,变量本身不会被认定无效。因为"引用"并没有在栈中复制变量的值;
2、引用不会获得值的所有权。引用只能租借(Borrow)值的所有权。引用本身也是一个类型并具有一个值,这个值记录的是别的值所在的位置,但引用不具有所指值的所有权;

fn main() {
    let s1 = String::from("hello");
    let s2 = &s1;
    let s3 = s1;
    println!("{}", s2);
}
//注意 因为 s2 租借的 s1 已经将所有权移动到 s3,所以 s2 将无法继续租借使用 s1 的所有权。如果需要使用 s2 使用该值,必须重新租借,如下

fn main() {
    let s1 = String::from("hello");
    let mut s2 = &s1;
    let s3 = s1;
    s2 = &s3;        // 重新从 s3 租借所有权
    println!("{}", s2);
}

3、租借的所有权不能修改所有者的值,既然引用不具有所有权,即使它租借了所有权,它也只享有使用权。
4、可变引用与不可变引用相比除了权限不同以外,可变引用不允许多重引用,但不可变引用可以。

fn main() {
    let s1 = String::from("run");
    let s2 = &s1;
    println!("{}", s2);
    s2.push_str("oob"); // 错误,禁止修改租借的值
    println!("{}", s2);
}
//当然加上 mut表示可变变量时候,是允许修改的
fn main() {
    let mut s1 = String::from("run");
    // s1 是可变的

    let s2 = &mut s1;
    // s2 是可变的引用

    s2.push_str("oob");
    println!("{}", s2);
}

4.1、垂悬引用

在C++中就是:放在有指针概念的编程语言里它就指的是那种没有实际指向一个真正能访问的数据的指针(注意,不一定是空指针,还有可能是已经释放的资源),Rust中不允许这样情况发生。
典型的就是在一个函数定义一个局部变量,函数返回值是一个引用,讲局部变量引用返回这种情况。

5、函数中变量

5.1 参数变量

函数中变量释放机制和上面介绍的一样,注意品味下面的。

fn main() {
    let s = String::from("hello"); // s 被声明有效
    takes_ownership(s); // s 的值被当作参数传入函数, 所以可以当作 s 已经被移动,从这里开始已经无效
    let x = 5;          // x 被声明有效
    makes_copy(x);      // x 的值被当作参数传入函数
                        // 但 x 是基本类型,依然有效
                        // 在这里依然可以使用 x 却不能使用 s
} // 函数结束, x 无效, 然后是 s. 但 s 已被移动, 所以不用被释放

fn takes_ownership(some_string: String) { // 一个 String 参数 some_string 传入,有效
    println!("{}", some_string);
} // 函数结束, 参数 some_string 在这里释放

fn makes_copy(some_integer: i32) {// 一个 i32 参数 some_integer 传入,有效
    println!("{}", some_integer);
} // 函数结束, 参数 some_integer 是基本类型, 无需释放

5.2 、返回值变量

被当作函数返回值的变量所有权将会被移动出函数并返回到调用函数的地方,而不会直接被无效释放。

fn main() {
    let s1 = gives_ownership(); // gives_ownership 移动它的返回值到 s1
    let s2 = String::from("hello"); // s2 被声明有效
    let s3 = takes_and_gives_back(s2);// s2 被当作参数移动, s3 获得返回值所有权
} // s3 无效被释放, s2 被移动, s1 无效被释放.
fn gives_ownership() -> String {
    let some_string = String::from("hello"); // some_string 被声明有效
    return some_string; // some_string 被当作返回值移动出函数
}
fn takes_and_gives_back(a_string: String) -> String { 
    // a_string 被声明有效
    a_string  // a_string 被当作返回值移出函数
}

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

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

相关文章

Python|Pyppeteer启动浏览器窗口,右侧出现空白区域怎么解决?(13)

前言 本文是该专栏的第13篇,结合优质项目案例持续分享Pyppeteer的干货知识,记得关注。 有些同学可能在使用pyppeteer的时候,在配置项里面,明明已经设置好了窗口最大化,而启动Chromium窗口,打开的窗口最右侧却是一大片空白区域,具体如下图所示: 那么,出现上述情况,需…

AutoGPT 英文版安装过程

自从2022年11月chatGPT的发布3.0GPT大模型,在中国掀起一股AI学习热潮,国内百度2023年4月份发布文心一言,把AI推上另一个高潮,最直接的是问答,我输入一句话,AI帮生成一段文字或一个视频,但是国内…

畅捷通T+ 反序列化漏洞复现(QVD-2023-13615)

0x01 产品简介 畅捷通 T 是一款基于互联网的新型企业管理软件,功能模块包括:财务管理、采购管理、库存管理等。主要针对中小型工贸和商贸企业的财务业务一体化应用,融入了社交化、移动化、物联网、电子商务、互联网信息订阅等元素。 0x02 漏…

Chat2DB数据AI工具开源!对数据分析师行业影响如何?

大家好,我是千与千寻,千寻目前在互联网公司担任算法工程师,也要经常性的和数据打交道。那么数据都存放在哪里?当然是数据库啦! 说到数据库,我们就不得不提到一种编程语言——SQL数据语言,后端程…

2023年最新项目管理工具排名推荐,助你提升项目效率!

在当今快速发展的互联网时代,项目管理工具已经成为了越来越多企业和团队必不可少的工具之一。好的项目管理工具能够帮助团队更加高效地协同工作,提高工作效率,节省时间和成本,从而使得整个项目可以更快地达成预期目标。现在让我们…

微信为什么使用 SQLite 保存聊天记录?

概要 SQLite 是一个被大家低估的数据库,但有些人认为它是一个不适合生产环境使用的玩具数据库。事实上,SQLite 是一个非常可靠的数据库,它可以处理 TB 级的数据,但它没有网络层。接下来,本文将与大家共同探讨 SQLite 在…

【Diffusion模型系列1】DDPM: Denoising Diffusion Probabilistic Models

0. 楔子 Diffusion Models(扩散模型)是在过去几年最受关注的生成模型。2020年后,几篇开创性论文就向世界展示了扩散模型的能力和强大: Diffusion Models Beat GANs on Image Synthesis(NeurIPS 2021 Spotlight, OpenAI团队, 该团队也是DALLE-2的作者)[1] Various…

阿里云国际站代理商:如何优化阿里云服务器的性能和响应速度?有哪些调优策略和建议?

随着互联网的发展,阿里云服务器已经成为很多企业和个人的首选解决方案。然而,面对不断增长的需求和复杂的网络环境,如何优化阿里云服务器的性能和响应速度,提高用户体验,是很多用户关心的问题。本文将从以下几个方面&a…

上海阿里云代理商:如何保护阿里云服务器中的敏感数据?有哪些加密和访问控制措施?

如何保护阿里云服务器中的敏感数据?有哪些加密和访问控制措施?   一、阿里云服务器安全概述   阿里云服务器作为云计算服务的主要产品,其安全性备受用户关注。在实际使用中,保护服务器中的敏感数据是至关重要的,而…

Tkinter之GUI界面布局介绍

Tkinter之GUI界面布局介绍 关于Python 的Tkinter窗口基础可参见https://blog.csdn.net/cnds123/article/details/127227651 Tkinter 本身没有提供拖拽放置控件的方式创建 GUI 界面,而是提供了pack、grid和place三种几何管理器(geometry manager&#x…

canvas详解05-变形

几何变换 canvas现在被大量地运用于游戏等动画领域,最主要的归功于它提供的一系列几何变换方法,使得动画更加地容易。所以其几何变换是非常重要的一节。 在本教程前面的部分中,我们已经了解了 Canvas 网格和坐标空间。到目前为止,我们只是根据我们的需要使用默认的网格,改…

Unity - 记一次,使用 RenderDoc 调试 渲染 异常 的过程

文章目录 vertex shader - rawfragment shader - raw调试RDC的shader准备选项 - remote - 不要选local先查看 texture, sampler, ubo 数据调试:输出原始的法线数据调试:输出原始法线0\~1>-1\~1,并应用法线强度的法线调试:输出世…

强化学习从基础到进阶-常见问题和面试必知必答[3]:表格型方法:Sarsa、Qlearning;蒙特卡洛策略、时序差分等以及Qlearning项目实战

【强化学习原理项目专栏】必看系列:单智能体、多智能体算法原理项目实战、相关技巧(调参、画图等、趣味项目实现、学术应用项目实现 专栏详细介绍:【强化学习原理项目专栏】必看系列:单智能体、多智能体算法原理项目实战、相关技巧…

一文了解RabbitMQ安装使用

什么是RabbitMQ? 官网:Messaging that just works — RabbitMQ RabbitMQ是一种开源的消息中间件软件,用于构建可扩展的分布式应用程序。它实现了高级消息队列协议(AMQP),这是一种网络协议,用于在应用程序之…

RIS 系列:TransVG: End-to-End Visual Grounding with Transformers 论文阅读笔记

RIS 系列:TransVG: End-to-End Visual Grounding with Transformers 论文阅读笔记 一、Abstract二、引言三、相关工作3.1 视觉定位两阶段方法单阶段方法 3.2 Transformer视觉任务中的 Transformer视觉-语言任务中的 Transformer 四、视觉定位中的 Transformer4.1 基…

【网络协议详解】——IPv4(学习笔记)

目录 🕒 1. IPv4地址概述🕒 2. 分类编址🕒 3. 划分子网🕘 3.1 概述🕘 3.2 如何实现🕘 3.3 无分类编址🕘 3.4 应用规划🕤 3.4.1 定长的子网掩码FLSM(Fixed Length Subnet …

【排序算法】冒泡排序、选择排序、插入排序

冒泡排序 依次比较相邻的两个元素,将比较小的数放在前面,比较大的数放在后面,直到所有元素排列完。 最容易理解的版本 对一个数组的n个整型数据进行n趟排序,每趟排序都尝试将较大值放到数组右侧。 每趟排序比较两个相邻的数据&…

相机模型概述

相机模型 如图:假设P是现实世界中的一个点,P是三维世界中的点 Pr(Xr,Yr,Zr) 光心O视作摄像头 Pc(Xc,Yc,Zc) 在相机平面中,Pc的坐标为(0,0,0) 在物理成像平面 Pp(Xp,Yp,0) 在像素平面 P(Xp,Yp,0) 但是!!! 到了像素平面,坐标就不一样了,像素平面坐标顶点(最左上角)才是…

【Java系列】深入解析 Lambda表达式

序言 你只管努力,其他交给时间,时间会证明一切。 文章标记颜色说明: 黄色:重要标题红色:用来标记结论绿色:用来标记一级论点蓝色:用来标记二级论点 希望这篇文章能让你不仅有一定的收获&#xf…

揭秘ChatGPT背后的传奇崛起,探索其引爆引爆网络的隐藏故事

文章目录 前言一、ChatGPT的诞生背景二、ChatGPT的技术原理三、ChatGPT的推广策略四、ChatGPT的未来展望五、橙子送书第3期 前言 ChatGPT是一款基于人工智能技术的聊天机器人,它的出现引起了广泛的关注和热议。在短短的时间内,ChatGPT就成为了全球范围内…