前言
提示:本文旨在记录一些本人在学习Rust中遇到的一些零碎的知识点,暂时不知道如何做知识分类,不成体系,读者可以选择行忽略:
小知识点
不能单独实现Copy,想要实现Copy必须连同Clone一起实现,推测Copy的实现是建立在Clone上的,而Clone可以单独被实现,各位读者可以自行测试。
形象化认识实现了Copy Trait和未实现Copy Trait的对象赋值的区别
Rust 中非CopyTrait的对象赋值时所有权转移动画
Rust中数组是有类型,类型格式形如 [i32;3]. 值得注意的是:[i32;3]与[i32;4] 不是一个类型
已知数组根据内部元素的类型和数量,会形成不同具体的类型,具体数组类型是否实现Copy Trait 取决于内部元素是否实现Copy Trait,满足条件,编译器会自动实现Copy Trait;相对地,struct实现Copy Trait必须程序员显式进行声明,并且满足保证内部元素(字段)全都实现Copy Trait这一条件;
枚举变体内变量 在if语句中需要使用let语句提示;在match里面不需要
在Rust中,dyn MyTrait 与·impl MyTrait是什么意思,有什么区别
值得注意的是,dyn MyTrait出现在结构体的字段当中;而impl MyTrait出现在方法的参数中
trait Chinese{
fn celebrate_on_10.1 ();
}
struct Company{
employee_nation:dyn Chinese
}
impl Company {
emloy_someone(someone:impl Chinese)
{
...
}
}
各位可以自行测试一下 ,互相颠倒使用dyn和impl,编译器会报错。
这时候我们会发现,dyn和impl区别是什么,是一个值得探讨的问题。
首先我们认识,在C#和Java当中,允许Interface v= new Implement(); 这种多态语句的,这是面向对象的设计; 而Rust的则不允许;内存中的分配对象let v:Struct=Struct{} ,即等式左边不是指针与对象,而是指v就是Struct本身(注意:此处Trait本质上是一个指针);在这个基础上如何实现实多态呢? Rust中的多态是有性的,只能是作为struct的字段存在,也就是说dyn Trait就是一个虚表,这个虚表指向了一个具体的实例;let v=Struct{iner:StructImpleteTrait} 此时Struct的大小确定了,到时候用到iner字段了通过虚表 会指向具体的实例;简而言之:dyn Trait是一个指针;很多人可能会想,为什么不把dyn Trait简化成 Trait。我猜想是设计者想让我们了解这个指针的动态性质吧。
现在轮到impl Trait,主要为了实现函数多态。我们先讲C#中函数的多态
Interce IAnimal{
}
class Cat{
}
class MyClass{
public void DoSomething (IAnimal animal){
...
}
}
前面已经说了 接口是一个虚表,调用的时候把Cat对象或者其他实现中IAnimal的虚表传入,就是实现了多态;如果Rust可以是同样的设计,此处的IAnimal 应该替换成dyn Trait书,但实际上Rust用的impl Trait ,Impl提示我们此处传入是具体的实现,不再是虚表了;聪明的你应该已经想到了,是编译器根据具体调用传入,针对这个对象编译一个新的方法,这个发生在编译时;好处就是,运行时没有任何的性能损耗。
显式声明的两种方法数据类型的两种方法
clone这个方法的编译器的类型推断逻辑
先看现象:
Rust项目依赖的两种方式
关于枚举
数值集合枚举(Unit-Like Enums)
数值型枚举第一个必须默认是0,需要指定其他数值需要显式指明;
第二个数值在第一个的基础上累加,依此类推。
由于第二个必须比第二个数值大,所以除了第一个,其他变体不能为0。
数值型枚举直接转化成数值
数据携带枚举 Data-Carrying Enums
容器型枚举是指,每个变体可以某个数据类型的容器(如i23),容器型和数据型是对立的,不能出现在同一个枚举中。
智能指针
impl<T: ?Sized> Deref for &T {
type Target = T;
fn deref(&self) -> &T {
*self
}
}
自动解引用或者说内存穿透的作用,应用于如下场合:
let mut mystr=Box::new(String::from("hello"));
mystr.push_str("123");
其中push_str()这个方法,属于String这个对象,而使用Box的对象能够调用。等等,前文我们不是说,会递归解引用最后一个引用吗?那这里应该是递归到&str结束。让我们来纠正一下,其实不需要递归到&str,编译器会判断push_str属于String这个对象,Sting这个智能指针就停止了。
我们有如下结论,自动接引用是方便程序员调用引用链条中某一环节的方法;
另一个场景是函数入参的时候自动解引用
let mut mystr=Box::new(String::from("hello"));
fn auto_deref (str: &str) {}
auto_deref(&mystr);
auto_deref(&String::from("1"));
明明是auto_deref函数定义的参数类型为&str,mystr是Box,但是我们可以通过给引用Box(看似多了一层引用,实际上引用遇到智能指针,触发了自动解开引用)获得了&str。
另外,通过这个案例就能明白,我们在学习Rust一开始就会有的疑惑:为什么fn func_name(str:&str)这样的函数签名可以接受func_name(&String::from("111")),答案就是自动解开引用;
值得注意的是,自动解引用的痛点在于解引用的最后,还是的形式是&val,如果想让而Box::new(Box::new(1)),如果想要实现内部数据+1,+是属于1本身方法 ,而不是&1的方法, 所以修改内部数据,必须使用*,在*参与的情况下,没有自动解引用。如果你必须利用自动解引用的特性的话。就得利用函数入参数自动解引用的特性,转门为自动解引用写一个方法。
Box
rust中使用关键字box 指定内存到堆中
let a=box 1;
智能指针Box内部使用了box关键字;如果对象没有创建,则会直接分配到堆中; 如果对象已经创建在栈中,则会将对象剪切到堆中;
Box会对数据对象产生独占效应。
结构体可以没有体「{}」