wordpress安装论坛,电脑优化是什么意思,做芯片外贸生意上哪个网站,河南省建筑资质查询【图书介绍】《Rust编程与项目实战》-CSDN博客
《Rust编程与项目实战》(朱文伟#xff0c;李建英)【摘要 书评 试读】- 京东图书 (jd.com)
Rust到底值不值得学#xff0c;之一 -CSDN博客
Rust到底值不值得学#xff0c;之二-CSDN博客
Rust的数据类型-CSDN博客
3.7 常…【图书介绍】《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所以不指定类型的话只有33Rust就不知道它的精度是多少于是let a33会自动将a推断成i32。总之对于没有明确指定整数类型的整数且其值在i32范围内Rust默认就认为它是i32类型。如果超出i32范围且在i64范围内则默认它是i64类型。
如果在整数后面加上u16类型那么Rust就知道这是个u16类型的整数。对于let a33u16可以知道a是一个u16类型的变量这和let a:u1633的作用相同。当然let a:u1633u16也可以只不过有点多此一举。但是let a:u1633u32这种方式不可行因为前后矛盾了。同样如果在整数后面加上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{}这样用来定义数据类型的大括号不在讨论范围之内本章后面所说的大括号也都不考虑这种大括号。以下几种结构中的大括号都有自己的作用域
1if、while等流程控制语句中的大括号。
2match模式匹配的大括号。
3单独的大括号。
4函数定义的大括号。
5mod定义模块的大括号。
例如可以单独使用一个大括号来开启一个作用域
{ // 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 OutLIFO的数据结构栈中的所有数据都必须占用已知且固定大小的内存。堆就好理解了它是一个没有组织的结构想怎么使用就怎么使用只要堆够大就可以申请一段内存空间然后把这段内存标记为已使用并得到指向这段内存开头的指针当不再使用时再将这段内存标记为未使用。当声明一个指针但并没有分配空间时这个指针是空指针当内存已经标记为未使用而指针依然指向这段空间时这个指针就是野指针。
C中的堆和栈定义如下。
堆由程序员手动分配和释放完全不同于数据结构中的堆分配方式类似于链表。由malloc或者new来分配由free和delete来释放。若程序员不释放则程序结束时由系统释放。栈由编译器自动分配和释放存放函数的参数值、局部变量的值等。栈里面变量的内存必须是已知且固定大小的。函数调用时参数、本地变量、指向堆的指针都压入一个栈函数完成时退出。操作方式类似于数据结构中的栈C和Python中也有只要基于C的都有这个概念。
其实分辨起来很容易动态分配的变量就是在堆上其他的都在栈上。
栈是一个成熟的结构基本不会引发内存的问题而没有组织的堆却很容易引发内存问题。垃圾回收和所有权都是为了解决堆的内存管理问题。
这就是C相比于垃圾回收机制语言的优势灵活高效但是也会带来内存安全问题。
3.9.2 什么是所有权
计算机程序必须在运行时管理它们所使用的内存资源。大多数编程语言都有管理内存的功能C/C这样的语言主要通过手动方式管理内存开发者需要手动申请和释放内存资源。但为了提高开发效率只要不影响程序功能的实现许多开发者没有及时释放内存的习惯。所以手动管理内存的方式常常造成资源浪费。
Java语言编写的程序在Java虚拟机Java Virtual MachineJVM中运行JVM具备自动回收内存资源的功能。但这种方式常常会降低运行时效率所以JVM会尽可能少地回收资源这样也会使程序占用较大的内存资源。
所有权对大多数开发者而言是一个新颖的概念它是Rust语言为高效使用内存而设计的语法机制。所有权概念是为了让Rust在编译阶段更有效地分析内存资源的有用性以实现内存管理而诞生的概念。
Rust的所有权是一个跨时代的理念是内存管理的第二次革命。较低级的语言依赖程序员分配和释放内存一不小心就会出现空指针、野指针破坏内存较高级的语言使用垃圾回收机制管理内存在程序运行时不断地寻找不再使用的内存虽然安全却加重了程序的负担。Rust的所有权理念横空出世通过所有权系统管理内存编译器在编译时会根据一系列的规则进行检查在运行时所有权系统的任何功能都不会减慢程序把安全的内存管理推向了零开销的新时代。
所有权概念是Rust语言的一个重要特性因为通过它才使得Rust的“安全”“高并发”得以发挥出优势。因为它让Rust无须垃圾回收即可保障内存安全。对于C/C程序员来说可能一直在跟内存安全打交道如内存泄露、智能指针等。对于别的语言来说会有垃圾回收机制。例如Python的垃圾回收机制有“标记清除”“分代回收”等方式。这两种方式各有优缺点。Rust则是通过所有权和借用来保证内存安全的。很多人不理解为什么Rust是内存安全的其实就是在默认情况下是写不出内存不安全的代码的。
Rust的所有权并不难理解它有且只有如下三条规则
1Rust中的每个值都有一个被称为其所有者的变量即值的所有者是某个变量。
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是哪个堆中实际数据的所有者。但无论如何描述都需要理解所有者和值之间的真相。