网站设计一般多少钱,西安做网站建设哪家好,潍坊百度推广优化,黄岗住房和城乡建设厅官方网站Rust堆栈
Rust中各种类型的值默认都存储在栈中#xff0c;除非显式地使用Box::new()将它们存放在堆上#xff0c;但数据要存放在栈中#xff0c;要求其数据类型的大小已知。对于静态大小的类型#xff0c;可直接存储在栈上#xff0c;如裸指针、布尔、字符、整数浮点数除非显式地使用Box::new()将它们存放在堆上但数据要存放在栈中要求其数据类型的大小已知。对于静态大小的类型可直接存储在栈上如裸指针、布尔、字符、整数浮点数数组等。动态大小的Vec、string都是存堆的一些注意事项
栈中的数据赋值给变量的时候数据是直接放在栈中的。类型的值都默认放在栈中所以创建引用的时候引用的是栈里的值。容器中保存的是原始类型的栈里的值或者指向堆数据的引用字符串字面量static静态变量都会硬编码嵌入到二进制程序的全局内存区const定义的常量会在编译期间直接以硬编码的方式内联插入到使用常量的地方即直接硬编码到对应代码行。同时函数也可以内联即函数对应代码体会直接展开并插入到调用函数的地方省去调用函数的开销。
位置与值
位置某一块内存位置它有自己的地址有自己的空间有自己所保存的值。值存储到位置中的数据(即保存在内存中的数据)位置的产生
会产生变量的时候初始化需要保存某个值的时候函数调用参数和返回值产生新的值引用解引用
let 语句
let a 1;a为变量名也是对内存位置的一个可读代号编译期间会被替换为更低级的代号或者直接为地址。也就是位置是存值1的一块内存。每个位置就是它所放值的所有值因为每个值都只能存放在一个位置中所以每个值都只能有一个所有者。let v vec![1, 2, 3, 4];v位置代表栈中的一块内存值是一个指针地址实际数据是放在堆里的。 引用
Rust的引用是一种原始数据类型位置仍然是栈里保存的值和指针一样是一个地址。该地址指向了**位置**也就是前面的a和v如
let n 33; // 假设n的地址为0x234
let nn n; // 假设nn的地址为0x123那么nn的位置是0x123它存的值是0x234也就是n的地址。编译器维护栈内存所以它知道栈中的某个内存是否安全而堆内存由程序员自己负责程序员自己的行为是无法保证安全的。所以Rust的行为模式是将是涉及到内存安全的概念扔到栈上让程序员远离对堆的操作。所以允许允许对栈中同一个数据的多个指向不允许对堆中同一个内存的多个指向即变量存在多个引用但所有权只能有一个。 位置的属性
位置的属性和状态都由编译器在编译期进行维护。位置有类型有标记是否被引用可变引用还是不可变共享还是独占等等根据位置的类型是否实现Copy Trait来决定该位置的是拷贝还是移走。 所有权和借用 变量作用域
我们知道rust 变量在脱离作用域之后就会被销毁但事实是变量在跳出作用域时会自动Drop Trait的drop函数来销毁内存中堆和栈的数据全局内存中的数据是从程序启动到终止期间一直存在。另外rust的作用域为一对大括号{}大括号的作用域是可以访问大括号外部的变量而在函数的作用域内则不行这被称为捕获环境函数是不能捕获环境的而大括号可以捕获环境。
数据的拷贝
由于所有权问题和变量脱离作用域而引起的内存二次释放问题rust是不允许有两个指针同时指向同一块内存的。所以rust 没有浅拷贝和深拷贝的概念取而代之的是move、copy和clone。
move也就是转移所有权涉及到的过程是拷贝到目标变量同时会将原来的变量设置到未初始的状态。rust 默认使用的就是move。
当使用值的时候就会产生位置那么就会发生移动。解引用字段访问索引访问等都会隐式移动。
copy和move的区别就是拷贝之后原来的变量还是可以用。如果要使用copy就需要要拷贝的数据类型实现了Copy Trait手动实现的时候需要同时实现Clone Trait。cloneclone和copy很接近区别在于Copy时只拷贝变量本身的值如果这个变量指向了其它数据则不会拷贝其指向的数据。Clone时拷贝变量本身的值如果这个变量指向了其它数据则也会拷贝其指向的数据。
函数调用之后也是会转移所有权的有时候这样是很不方便的所以在传参的时候可以传递到变量的引用引用时保存在栈里也实现了Copy Trait这样效率会更高。 可变引用的排他性
不可变引用是可以共存的但是可变引用具有排他性在同一作用域同一数据只能有一个。这里的排他性应该看作一把独占锁在当前作用域内从第一次使用可变引用开始创建这把独占锁之后无论使用原始变量(即所有权拥有者)、可变引用还是不可变引用都会抢占这把独占锁以保证只有一方可以访问数据每次抢得独占锁后都会将之前所有引用变量给锁住使它们变成不可用状态。当离开当前作用域时当前作用域内的所有独占锁都被释放。
回顾一下可变引用的几个性质
同一作用域特定数据只能有一个可变引用可变借用不能用于不可变借用上有了可变借用就不能再有不可变借用引用作用域和变量作用域不一样它的结束位置再最后一次使用的位置
自从第一次使用可变引用导致独占锁出现后可以随时使用原始变量、可变引用或不可变引用来抢独占锁但抢锁后以前的引用变量就不能再用且当前持有的锁也可以随时被抢走。不可变引用抢占之后所有的包括自身都是不可用的但再次使用可变引用抢占锁之后该可变引用是可用的。一切都由程序员控制程序员可以在任意代码位置通过原始变量或引用来抢锁。 模式匹配
rust中可分为
不可反驳的模式(irrefutable)一定会匹配成功否则编译错误如let赋值for迭代函数传参等。可反驳的的模式(refutable)可以匹配成功也可以匹配失败匹配失败的结果是不执行对应分支的代码如if let 和 while let
match匹配支持两个模式
当明确给出分支的Pattern时必须是可反驳模式这些模式允许匹配失败使用_作为最后一个分支时是不可反驳模式它一定会匹配成功如果只有一个Pattern分支则可以是不可反驳模式也可以是可反驳模式
再谈Trait 组合
Trait最基本的作用是从多种类型中抽取出共性的属性或方法主要表现为泛型数据类型。可以理解为它描述了一种通用的功能功能都要求具有某些特殊的行为同时功能可以被很多种类型实现。同样一个类型也可以实现很多种Trait组合出很多功能这和一般面向对象编程语言的继承有所不同不用继承冗余的功能而更加的自由。组合和继承的关系可以理解为 **has a **和 is a 的关系。 特征对象
也就是具有某个特征功能的类的实例。上篇提到过Duck Typing 也就是只需要叫起来想鸭子就可以当成鸭子来使用。只需要你会打螺丝不管你是不是大学生。这里的意思就是实现了某个特征的众多对象都具有该功能而由于 Trait 自身不能当作数据类型来用因为可能一种类型实现了很多中 Trait显然无法用一种 Trait 来代替这种数据类型。因此就诞生了 Trait Object也就是将实现了 Trait A 的类型 B,C,D 当作 Trait A 的 Trait Object使用。可以类比为继承里的父类
Trait object的创建是通过dyn T或者指针Boxdyn TRcdyn T等等本质就是由于 Trait object 的大小是不定的所以选择将引用存在栈中包含两部分数据
指向数据的指针指向实现了Trait的具体类型的实例指向一个虚表 vtable 的指针因为实现了 Trait 的类型有很多而每个类型拥有的方法时各不相同的所以需要一个虚表来区分保存。虚表中保存了实例可以调用的实现的来自特征 Trait 的方法。当该对象调用方法时直接从虚表中找到方法然后调用。 其他
Struct和Enum类型需要手动实现Trait即使用#[derive()]特征是支持继承的如
trait B{}
trait A: B{}当类型想实现 Trait A 的时候需要要求同时实现 Trait B 参考
Rust入门秘籍