Rust的常数、作用域与所有权

【图书介绍】《Rust编程与项目实战》-CSDN博客

《Rust编程与项目实战》(朱文伟,李建英)【摘要 书评 试读】- 京东图书 (jd.com)

Rust到底值不值得学,之一  -CSDN博客

Rust到底值不值得学,之二-CSDN博客

Rust的数据类型-CSDN博客

3.7  常数的数据类型

在Rust语言中,变量有类型,常量也有类型。我们知道,在定义const常量的时候,就要确定数据类型,这个问题不大,而直接常量如何确定类型呢?首先想个问题,为什么要把常量分为不同的类型呢?这是因为在程序中出现的常量需要存放在计算机内存的存储单元中。如果确定了数据的类型,也就能确定应该分配给它多少字节,按什么方式存储。例如,程序中有整数100,默认情况下,Rust编译器会分配给它4字节,按补码方式存储。那么怎样确定常量的类型呢?

从常量的表示形式即可判定其类型。对于字符常量很简单,只要看到由单撇号引起来的单个字符或转义字符就是字符常量。下面我们来讲一下数值常量。

(1)整数。整数在Rust中有一种特殊的表达方式,比如let a =33u16,因为u8、i8、u16、i32等都可以表示33,所以不指定类型的话,只有33,Rust就不知道它的精度是多少,于是let a=33会自动将a推断成i32。总之,对于没有明确指定整数类型的整数,且其值在i32范围内,Rust默认就认为它是i32类型。如果超出i32范围,且在i64范围内,则默认它是i64类型。

如果在整数后面加上u16类型,那么Rust就知道这是个u16类型的整数。对于let a=33u16,可以知道a是一个u16类型的变量,这和let a:u16=33的作用相同。当然,let a:u16=33u16也可以,只不过有点多此一举。但是let a:u16=33u32这种方式不可行,因为前后矛盾了。同样,如果在整数后面加上i8,且该整数没超出i8范围,就认为它是i8类型。

以上整数都是用十进制表示的,我们也可以使用二进制、八进制或十六进制创建整数,比如:

fn main() {let a = 33u16;let b: i32 = 0b11_01_10_11;                     // 二进制let c = 0o567i64;                                // 八进制let d = 0xFFFFu32;                               // 十六进制println!("{} {} {} {}", a, b, c, d);           // 输出:33 219 375 65535}

(2)浮点数。凡以小数形式或指数形式出现的实数,都是浮点型常量,在内存中都以指数形式存储。例如,10是整型常量,10.0是浮点型常量。那么对浮点型常量是按f32处理还是按f64处理呢?Rust编译器把浮点型常量都按f64处理,分配8字节。

如果要明确指定浮点数类型,可以在后面加上类型,比如3.14159f32,这样就按f32处理了。

3.8  作  用  域

Rust的所有权系统和作用域息息相关,因此有必要先理解Rust的作用域规则。在Rust中,任何一个可用来包含代码的大括号都是一个单独的作用域。类似于Struct{}这样用来定义数据类型的大括号,不在讨论范围之内,本章后面所说的大括号也都不考虑这种大括号。以下几种结构中的大括号都有自己的作用域:

(1)if、while等流程控制语句中的大括号。

(2)match模式匹配的大括号。

(3)单独的大括号。

(4)函数定义的大括号。

(5)mod定义模块的大括号。

例如,可以单独使用一个大括号来开启一个作用域:

{                                         // s在这一行无效,因为它尚未声明let s = "hello";                     // 从这行起,s是有效的println!("{}", s);                   // 使用sprintln!("hello,world");            // 这行没有用到s,但s依然是有效的}                                         // 到了这行,此作用域已结束,s不再有效

上面的代码中,变量s绑定了字符串字面值,在跳出作用域后,变量s失效,变量s所绑定的值会自动被销毁。

实际上,变量跳出作用域失效时,会自动调用Drop trait的drop函数来销毁该变量绑定在内存中的数据,这里特指销毁堆和栈上的数据,而字符串字面量是存放在全局内存中的,它会在程序启动到程序终止期间一直存在,不会被销毁。可通过如下代码验证:

fn main(){{let s = "hello";println!("{:p}", s);  // 0x7ff6ce0cd3f8}let s = "hello";println!("{:p}", s);  // 0x7ff6ce0cd3f8}

因此,上面的示例中只是让变量s失效了,仅此而已,并没有销毁s所绑定的字符串字面量。但一般情况下不考虑这些细节,而是照常描述为跳出作用域时,会自动销毁变量所绑定的值。

任意大括号之间都可以嵌套。例如,可以在函数定义的内部再定义函数,在函数内部使用单独的大括号,在函数内部使用mod定义模块,等等。示例如下:

fn main(){fn ff(){println!("hello world");}ff();let mut a = 33;{a += 1;}println!("{}", a);  // 结果输出:34}

虽然任何一种大括号都有自己的作用域,但函数作用域比较特别。在函数作用域内无法访问函数外部的变量,而其他大括号的作用域可以访问大括号外部的变量。比如:

fn main() {let x = 32;fn f(){// 编译错误,不能访问函数外面的变量x和y// println!("{}, {}", x, y); }let y = 33;f();let mut a = 33;{// 可以访问大括号外面的变量aa += 1;}println!("{}", a);  //结果输出:34}

在Rust中,能否访问外部变量称为捕获环境。比如,函数是不能捕获环境的,而大括号可以捕获环境。对于可捕获环境的大括号作用域,要注意Rust的变量遮盖行为。分析下面的代码:

fn main(){let mut a = 33;{a += 1;           // 访问并修改外部变量a的值// 又声明变量a,这会发生变量遮盖现象// 从此开始,大括号内访问的变量a都是该变量let mut a = 44;a += 2;println!("{}", a);             // 输出46}                                 // 大括号内声明的变量a失效println!("{}", a);               // 输出34}

这种行为和其他语言不太一样,因此这种行为需要引起注意。

3.9  所  有  权

3.9.1  让我们回忆栈和堆

在学习C/C++时,老师经常出某变量被分配在栈上还是堆上的题目,几乎每次都有很大一批同学在这种题目上失手。栈和堆都是代码在运行时可供使用的内存,但是它们的结构不同。栈是一种后进先出(Last In First Out,LIFO)的数据结构,栈中的所有数据都必须占用已知且固定大小的内存。堆就好理解了,它是一个没有组织的结构,想怎么使用就怎么使用,只要堆够大,就可以申请一段内存空间,然后把这段内存标记为已使用,并得到指向这段内存开头的指针;当不再使用时,再将这段内存标记为未使用。当声明一个指针但并没有分配空间时,这个指针是空指针;当内存已经标记为未使用,而指针依然指向这段空间时,这个指针就是野指针。

C++中的堆和栈定义如下。

  • 堆:由程序员手动分配和释放,完全不同于数据结构中的堆,分配方式类似于链表。由malloc或者new来分配,由free和delete来释放。若程序员不释放,则程序结束时由系统释放。
  • 栈:由编译器自动分配和释放,存放函数的参数值、局部变量的值等。栈里面变量的内存必须是已知且固定大小的。函数调用时,参数、本地变量、指向堆的指针都压入一个栈,函数完成时退出。操作方式类似于数据结构中的栈(C和Python中也有,只要基于C的都有这个概念)。

其实分辨起来很容易,动态分配的变量就是在堆上,其他的都在栈上。

栈是一个成熟的结构,基本不会引发内存的问题,而没有组织的堆却很容易引发内存问题。垃圾回收和所有权都是为了解决堆的内存管理问题。

这就是C++相比于垃圾回收机制语言的优势,灵活高效,但是也会带来内存安全问题。

3.9.2  什么是所有权

计算机程序必须在运行时管理它们所使用的内存资源。大多数编程语言都有管理内存的功能:C/C++这样的语言主要通过手动方式管理内存,开发者需要手动申请和释放内存资源。但为了提高开发效率,只要不影响程序功能的实现,许多开发者没有及时释放内存的习惯。所以手动管理内存的方式常常造成资源浪费。

Java语言编写的程序在Java虚拟机(Java Virtual Machine,JVM)中运行,JVM具备自动回收内存资源的功能。但这种方式常常会降低运行时效率,所以JVM会尽可能少地回收资源,这样也会使程序占用较大的内存资源。

所有权对大多数开发者而言是一个新颖的概念,它是Rust语言为高效使用内存而设计的语法机制。所有权概念是为了让Rust在编译阶段更有效地分析内存资源的有用性以实现内存管理而诞生的概念。

Rust的所有权是一个跨时代的理念,是内存管理的第二次革命。较低级的语言依赖程序员分配和释放内存,一不小心就会出现空指针、野指针破坏内存;较高级的语言使用垃圾回收机制管理内存,在程序运行时不断地寻找不再使用的内存,虽然安全,却加重了程序的负担。Rust的所有权理念横空出世,通过所有权系统管理内存,编译器在编译时会根据一系列的规则进行检查,在运行时,所有权系统的任何功能都不会减慢程序,把安全的内存管理推向了零开销的新时代。

所有权概念是Rust语言的一个重要特性,因为通过它才使得Rust的“安全”“高并发”得以发挥出优势。因为它让Rust无须垃圾回收,即可保障内存安全。对于C/C++程序员来说,可能一直在跟内存安全打交道,如内存泄露、智能指针等。对于别的语言来说,会有垃圾回收机制。例如Python的垃圾回收机制,有“标记清除”“分代回收”等方式。这两种方式各有优缺点。Rust则是通过所有权和借用来保证内存安全的。很多人不理解为什么Rust是内存安全的,其实就是在默认情况下,是写不出内存不安全的代码的。

Rust的所有权并不难理解,它有且只有如下三条规则:

(1)Rust中的每个值都有一个被称为其所有者的变量(即值的所有者是某个变量)。

(2)值在任一时刻有且只有一个所有者。

(3)当所有者(变量)离开作用域时,这个值将被销毁。

这里对第三点做一些补充性的解释,所有者离开作用域会导致值被销毁,这个过程实际上是调用一个名为drop的函数来销毁数据释放内存的。在前面解释作用域规则时曾提到过,销毁的数据特指堆栈中的数据,如果变量绑定的值是全局内存区内的数据,则数据不会被销毁。例如:

fn main(){{let mut s = String::from("hello");} // 跳出作用域,栈中的变量s将被销毁,其指向的堆中的数据也被销毁// 但全局内存区的字符串字面量仍被保留}

Rust中的每个值都有一个所有者,但这个说法比较容易产生误会。例如:

#![allow(unused)]fn main() {let s = String::from("hello");}

很多人可能会误以为变量s是堆中字符串数据hello的所有者,但实际上不是。String字符串的实际数据在堆中,但是String大小不确定,所以在栈中使用一个胖指针结构来表示这个String类型的数据,这个胖指针中的指针指向堆中的String的实际数据。也就是说,变量s的值是那个胖指针,而不是堆中的实际数据。因此,变量s是那个胖指针的所有者,而不是堆中实际数据的所有者。但是,由于胖指针是指向堆中数据的,很多时候为了简化理解,简化描述方式,经常会说s是哪个堆中实际数据的所有者。但无论如何描述,都需要理解所有者和值之间的真相。

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

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

相关文章

HTTP 二、进阶

四、安全 1、TLS是什么 (1)为什么要有HTTPS ​ 简单的回答是“因为 HTTP 不安全”。由于 HTTP 天生“明文”的特点,整个传输过程完全透明,任何人都能够在链路中截获、修改或者伪造请求 / 响应报文,数据不具有可…

阿里不认命

​ 转载:新熵 原创 作者丨萧维 编辑丨影蕨 国家定调了!一系列积极信号为平台经济注入一剂强心针,阿里迎来新生。 最近,阿里捷报频传! 先是8月28日,阿里巴巴完成香港双重主要上市。紧接着,8月…

基于聚类与LSTM对比特币价格深度分析与预测

1.项目背景 比特币作为全球最具影响力的加密货币之一,其价格受到多种复杂因素的共同作用,包括市场情绪、政策变化、大型机构的投资行为等,这些因素在不同的市场阶段对比特币价格波动产生直接或间接的影响。通过对比特币市场的深入分析&#…

66城代表齐聚!蓝卓分享“全国经验”,批量复制推动中小企业数字化转型

9月6日下午,2024中小企业数字化转型现场交流活动在浙江宁波隆重举行。 全国66个中小企业试点城市500多名中小企业主管部门及专家学者,制造业企业、数字化转型服务商等重点企业代表齐聚宁波,共同探讨中小企业数字化转型的模式和路径。 工业和…

酒店智能轻触开关:智慧化的创新实践

在追求高品质住宿体验的今天,酒店智能轻触开关作为智慧酒店建设的关键一环,正逐步成为提升酒店服务品质、优化运营效率、增强顾客满意度的有力工具。本文将深入探讨酒店智能轻触开关如何助力酒店实现智慧化管理,以及它所带来的多重变革。 一、…

VSCode连接docker

1.启动ssh服务 vim /root/.bashrc 或者 vim ~/.bashrc /usr/sbin/sshd #启动ssh服务~代表主目录,cd ~会返回root目录 cd / 返回最根上的目录 为了防止每次打开容器都要输入此指令,我们直接在 ~/.bashrc文件最后一行添加sshd启动命令即可。 打开终端…

【JAVA开源】基于Vue和SpringBoot的图书个性化推荐系统

本文项目编号 T 015 ,文末自助获取源码 \color{red}{T015,文末自助获取源码} T015,文末自助获取源码 目录 一、系统介绍1.1 业务分析1.2 用例设计1.3 时序设计 二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究…

掌握ChatGPT写论文六步提问法,会提问才能写出优质好论文

大家好,感谢关注。我是七哥,一个在高校里不务正业,折腾学术科研AI实操的学术人。关于使用ChatGPT等AI学术科研的相关问题都可以分享,相互成就,共同进步,为大家带来最酷最有效的智能AI学术科研写作攻略。 今天给大家分享的是借助GPT一年发两篇SCI的学术大拿总结的ChatGPT六…

IPS和IDS有啥区别?

吉祥知识星球http://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247485367&idx1&sn837891059c360ad60db7e9ac980a3321&chksmc0e47eebf793f7fdb8fcd7eed8ce29160cf79ba303b59858ba3a6660c6dac536774afb2a6330&scene21#wechat_redirect 《网安面试指南》…

ChatGPT付费创作系统V3.0.6独立版 WEB+H5+小程序端 (新增AI全网搜索+文档解析+豆包AI通道)安装部署教程

播播资源GPT付费体验系统最新版系统是一款基于ThinkPHP框架开发的AI问答小程序,是基于国外很火的ChatGPT进行开发的Ai智能问答小程序。这是一种基于人工智能技术的问答系统,可以实现智能回答用户提出的问题。相比传统的问答系统,ChatGPT可以更…

认识Linux及Linux的环境搭建

目录 1、什么是Linux2、Linux环境搭建2.1 下载安装 Xshell2.2 下载安装 VMware Workstation Pro2.3 选择适合自己系统 1、什么是Linux Linux,一般指GNU/Linux(单独的Linux内核并不可直接使用,一般搭配GNU套件,故得此称呼&#xff…

ARM基础知识---CPU---处理器

目录 一、ARM架构 1.1.RAM---随机存储器 1.2.ROM---只读存储器 1.3.flash---闪存存储器 1.4.时钟(振晶) 1.5.复位 二、CPU---ARM920T 2.1.R0~R12---通用寄存器 2.2.PC程序计数器 2.3.LR连接寄存器 2.4.SP栈指针寄存器 2.5.CPSR当前程序状态寄存…

java,php,go,nodejs,Python开发web项目优缺点对比

Java 优点:java 是一门广泛应用于企业级开发的语言,丰富且庞大的开发框架和库。有较高的性能和可伸缩性。生态系统庞大且成熟,拥有大量的开源框架和工具,可以加速开发过程。 内置对多线程的支持,适合处理高并发的 Web 项目。 缺点:相比其他语言,Java 的语法相对冗长繁琐…

【H2O2|全栈】关于Photoshop | PS(4)

PS的一些杂谈(亖) 目录 PS的一些杂谈(亖) 前言 准备工作 图形工具 基本属性 混合选项 形状图层 文字工具 基本属性 进一步变化文字 组和图层 UI设计案例 预告和回顾 后话 前言 这一篇博客我将会写一下图形工具和…

【C++】STL学习——priority_queue(了解仿函数)

目录 priority_queue介绍迭代器种类priority_queue实现仿函数仿函数的使用 priority_queue介绍 优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的。此上下文类似于堆,在堆中可以随时插入元素&#x…

Python | Leetcode Python题解之第394题字符串解码

题目&#xff1a; 题解&#xff1a; class Solution:def decodeString(self, s: str) -> str:def dfs(s, i):res, multi "", 0while i < len(s):if 0 < s[i] < 9:multi multi * 10 int(s[i])elif s[i] [:i, tmp dfs(s, i 1)res multi * tmpmulti…

SpringCache源码解析(三)——@EnableCaching

一、源码阅读 让我们进行源码阅读把。 1.1 阅读源码基础&#xff1a; Import(xxx.class)里的类可以有两种类&#xff1a; ImportSelector接口的实现类&#xff1b;ImportBeanDefinitionRegistrar接口的实现类&#xff1b; 两种接口简介&#xff1a; ImportSelector接口&am…

什么是 TDengine?

TDengine 是一款专为物联网、工业互联网等场景设计并优化的大数据平台&#xff0c;其核心模块是高性能、集群开源、云原生、极简的时序数据库。它能安全高效地将大量设备、数据采集器每天产生的高达 TB 甚至 PB 级的数据进行汇聚、存储、分析和分发&#xff0c;对业务运行状态进…

kaggle竞赛平台上数据集下载详解

引言 kaggle作为一个数据分析竞赛平台不仅可以上传代码和数据集&#xff0c;参与一些公开的竞赛&#xff0c;同时也可以下载别人上传的数据集。本文着重介绍如何注册kaggle账号&#xff0c;在本地机上安装kaggle API,以及从kaggle数据集界面上下载想要的数据集到指定位置。 文章…

腾讯面试:说说6大Nginx负载均衡?手写一下权重轮询策略?

尼恩说在前面 在40岁老架构师 尼恩的读者交流群(50)中&#xff0c;最近有小伙伴拿到了一线互联网企业如得物、阿里、滴滴、极兔、有赞、希音、百度、网易、美团的面试资格&#xff0c;遇到很多很重要的面试题&#xff1a; 1.讲一下什么是负载均衡&#xff0c;什么是轮询策略、…