年前最后一项技能树 Rust ,攻碉堡 ing (Bilibili 视频整理)
前言
我自己的语言语言学习树
最新的新闻
最近的新闻,Rust 审核团队,集体辞职抗议, 还是linux 社区的 linus 牛皮,镇得住场子不要慌,咱们是在linux 社区混的,影响不大,该学还是得学
先下个蛋, 再说,关键问题是内存模型差异巨大, 如果是纯软件,用就用了,驱动编程的接口就很尬,别慌慢慢来
- 做linux 嵌入式开发的话,就拿语言来讲,基本上是完整的, 可以对标linux 社区
- 为什么这样说,内核的调试开发引入了,python BCC 工具,目前是用的最多的
- C++ 在不考虑模板编程的情况下,和C 差异不大,但是算法提供的多,现成的数据结构也多,同时熟悉面向对象可以对内核编程有帮助
- Rust 现在小众,但是这个语言,在linux 社区里面 很火,他和C++ 的语法 相差不大,如果C++ 足够熟得话,学习成本较低
- 这3门语言,如果做Linux 嵌入式,主要是别人都会
- 在看脚本,为了简化工作量,单纯的人工测试,虽然没有运维的水平高,但是简化工作量,还是可以做到的,别人都会
- 工程管理,在linux gnu 开源代码里,用的是 Makefile,但是 大多是 autoconf ,生成的,或者 cmake,但是cmake 语法简单,学习成本低,主要是别人都会
- 日常工作, Markdown 可以快速生成pdf ,在有pdf 转化成 word 也可以 ,说明什么问题清楚明了,也主要是别人都会
- 完全不会的那些,stap 还好,ARM 不是难,是记忆的地方多,而且,得经常用,matlab 做个数据计算啥的方便,可以模拟一些基本电路,也不是难,关键是数学计算,现在忘的差不多了
- 可有可无系列,没啥意思的时候,可以鼓捣,如果不是专门做,我也不建议做,入门容易,但是非常熟练,得花大量时间,好玩是好玩,但是耗时间
- 算法训练,其实是重要,但是,有专门的算法岗,我不太清楚,日常和算法相关的很少,就算涉及了,也是涉及了很多,我们行业的算法,不是leetcode 中的算法,对编程水平的提升,的确有很大的帮助,脑子里有东西在写代码的时候
乍一看很多,其实很少,计算机语言,本来就是实现设计的工具,或进行计算的工具,真正复杂的地方,涉及到各种协议的算法,硬件也好,
软件也好,,大家都一样,那你做芯片的话,那你就真的很强,但是做linux 嵌入式还是看 linux 内核的熟悉程度
环境布置
- 建议在ubuntu 搭建环境
sudo apt install rustc # 一键补全rust 环境
#安装rustup
curl https://sh.rustup.rs -sSf | sh
测试 “hello world”
- 安装完成以后 就会使用 cargo 命令
- cargo 是一个工程管理工具
- cargo new hello 创建一个hello的工程
- 进入 src 文件夹
fn main(){println!("hello world\n");
}
- 编译命令
cargo run #就可以开始编译
Rust 语法讲解,官网有详细的文档
我用的是国内翻译好的书上的截图,官方文档是英文的,就这点区别,其他完全一样
vscode的 环境配置
Rust 插件
插件设置
选择 rls 完全够用
剩下的就是 ctrl+shift+p 选择 编译任务 和 ctrl+shift+b 开始 build
Rust 常见问题
1. failed to select a version for the requirement
rust 如果是这种问题,rust 本身也会提供解决问题
2. Blocking waiting for file lock on package cache
- 直接删除 %USERPROFILE%/.cargo/.package_cache
- 然后修改 %USERPROFILE%/.cargo/config 文件更换为国内源就看以解决这个问题
查看关于rust 工程依赖库的文档,很方便
cargo doc --open
关键语法 所有权 和 枚举 的使用
这个枚举有点骚
所有权的问题,是Rust 编程的核心,C 的核心是 指针 C++ 是模板
不同于C++ 地方
小点
这个分隔符号还是一个挺好的
函数和Python 有点像
变量
- let a; 这个 a 自身有块空间
- a = 3; 直接在 a 上赋值是错误的
- let b = a; 这种是可以的,为什么可以测试下
- 如果想直接赋值就要使用, mut 进行声明
- 是这个意思 let mut a = 3; a = 5; 这种就没问题
Rust 闭包 和 python 的很像
let f = |x| x + 1;
下面是PYTHON的 lambda
lambda arg1, arg2: arg1 + arg2 # arg1, arg2可以传入默认值
下面是C++的lambda 的表达式的方法
class A
{public:int i_ = 0;void func(int x, int y){auto x1 = []{ return i_; }; // error,没有捕获外部变量auto x2 = [=]{ return i_ + x + y; }; // OK,捕获所有外部变量auto x3 = [&]{ return i_ + x + y; }; // OK,捕获所有外部变量auto x4 = [this]{ return i_; }; // OK,捕获this指针auto x5 = [this]{ return i_ + x + y; }; // error,没有捕获x、yauto x6 = [this, x, y]{ return i_ + x + y; }; // OK,捕获this指针、x、yauto x7 = [this]{ return i_++; }; // OK,捕获this指针,并修改成员的值}
};
总结下,就是越来越懒人们
条件判断 和 python 有点像
if a > b{print!("hello world\n");
}
if c > d:print("hello world")
干脆 大括号也去了得了
for 循环的使用和python3 有点像
let array = [1,2,3,4,5];
for ele in array.iter() {print!("ele {}",ele);
}
a = [1,2,3,4,5]
for t in a:print(t)
官方说这样的好处在于,不会有访问越界的问题发生
Rust 中的堆栈 和 垃圾回收机制 (GC) Rust 实际上是没有 垃圾回收功能的
截图为敬
String 是根据字符串的大小在堆上开辟一小块内存
- 毕竟最终都是转化为机器指令的,堆栈是避免不了的
- rust 中虽然没有垃圾回收机制的,都是默认的释放堆上的资源
- 官方手册上的原话是:
- 内存会自动地在拥有它的变量离开作用域后进行释放
- 看到人家rust 手册。。。 。。。
- 意思就是限制到了作用域中了
C++ 中的析构的模式称为 RAII(Ressource Acquistion Is Initialization)
书中称该设计思想为Rust 设计思想的核心
书中关于变量和数据的交互方式
- 假如是一个简单的值或者字符(Rust 的 字符是4字节的 支持 unicode 编码,而C++ 和 C 都是 1字节的 utf-8 编码)
- 进行入栈操作的赋值
let x = 5;
let y = x;
意思就是 5 是一个数值,绑定到x 上, 就是发生了入栈了
y = x; y 会 拷贝 x 中的数值,并绑定到自己的身上, 这就和我们平常接触到的语言就一样了
- 上面的这些是Rust 为了高效的操作 使用了 trait 中的 copy
有一个基本的原则
- 所有的整数类型,i64 i32 i16 i8 u64 u32 u16 u8
- 所有的字符 char
- 所有的浮点类型 f64 f32
- 布尔类型 bool
在以上的规则中遵循 trait 的 copy 特性
- 但是String的情况就是在堆上,前面的过程不变,但是为了回收堆上的内存,作了新的决策
以下图为书中的原图,画的很清楚
图4-2
这个过程是必须要清楚的, Rust 中只是浅拷贝
这只是刚刚开始
Rust 的所有权规则
- Rust 中的每一个值都有一个对应的变量作为它的所有者 (每个变量的空间就是相对隔离的,其他语言也这样,但是Rust 执行的更加彻底)
- 在同一时间内,值有且仅有一个所有者 (“=” 会发生拷贝)
- 当所有者离开自己的作用域时,他持有的值就会被释放掉(记住前面绑定的概念)
Rust 默认的操作 都是高效的 ,默认即是灵魂
let s1 = String::from("Hello World");
let s2 = s1.clone() ; //这个是操作是深度拷贝的操作
- 深度拷贝,各自的指针指向各自的空间
Rust 中的元组 和 python 中的 元组 有点类似
let x:(u8,f32,i64) = ('a',3.1415926,-10000);
- 访问的形式有点怪, 数组的访问形式还是挺好的
- python3 中的元组结构
所有权和函数
将值传递给函数
进行赋值
将变量传递给函数触发移动或复制
看到了吧, 这就是Rust 的好处, 被移到了函数中,在函数中调用完以后其他地方就无法在使用了
- 这种返回方式 我都忍不住看看是不是 return 了, 人家就是没有return
返回值与作用域有一个基本原则
- 将一个值赋值给另一个变量时就会转移所有权,当一个持有堆数据的变量离开作用域时,它的数据就会被drop清理回收,除非又将所有权转到另一个变量上
Rust 的数组初始化 和数组的优化, 且不支持 自增和自减运算符
// 最正常的数组 python的 list 就长这个模样
let a = [1,2,3,4,5];
let b = [3;5]; //初始化 实际上是这个样子 [3,3,3,3,3]
let b: [i32;5] = [1,2,3,4,5]; //数组使用时必须进行初始化 否则 会报错,这点就比较好
- 红色波浪线就粗误,就是因为没有进行初始化,黄色波浪线就是告警, 访问和数组的访问是一样的
引用与借用
- 引用的概念和C++ 的引用差不多 (void * const ), 其实指针的存在,有很多的问题, 但是可以解决的问题大于存在的问题
- look 这就可以 就继续使用 str_v1 了
- 之前如果要返回必须要在函数中 返回才能用
- 引用的存在是为了不发生移动,转换所有权
书中的示例代码
- 返回的是参数的所有权,这个所有权就发生了转移,转移到了新的参数上
- 就是说引用没有转移使用权
- 借用
- 但是可变引用就可以解决这个问题
就可以修改了
注意事项
悬垂引用
这就相当nice, C++ C 弱爆了,Rust 不给你犯低级错误的机会
引用规则
切片的概念
- 一般性代码
- 这里面有迭代器,中由引用来借用元素
- 哇,return 活久见 只用于提前返回使用
- 上面代码只是为了对比说明 数组切片的意义
字符串切片
剩下的就是重复出错,查看错误现象了
- 切片存在的意义, 还是为了程序员不犯低级错误
- python 中的切片, 是为了方便截取字符筛选的, 妈的,这是德国制造吧
所有权的重要性
- 我理解的是内存空间的所有权,代表谁有权访问这块内存,谁有权修改,一旦离开作用域,在不采取引用的情况下,就会失效
- mut 是为了告诉可以不可以修改数据
- 总之就是严谨,规避了低级错误引发的大地震
Rust 中的 struct 使用
- C 和 C++ 都得 在声明 结构体时都得需要 struct name{ int a}; 加分号
- Rust 就不需要使用 结构体中的 分号
- 必须先获取结构体的实例才行,才能正常使用
- 结构体符合之前所有的规则
******************************************************** [20%] 分割线
函数中使用 struct
fn build_user(email: String,uername: String)-> User{ //并返回一个结构User{email: email,username: username,active:true,sign_in_count: 1,}
}
- 可以简化的方法
- 其他非著名使用方法
- 可以进少代码量,有点像C++ 的继承的方法, 但是Rust 不是面向对象的
- 元组式的 struct 访问
Rust 中通过派生 trait 增加使用的功能
- 这self 更像是 C++ 中的 this指针 和 python 对象中的 self 有异曲同工之妙
- 这其中还不包括 继承和特性的使用,
- 我会把 trait 归到这部分,现在先跳过
Rust 枚举的使用
- 是为了枚举使用的检测,检测枚举的情况,可以看到枚举的调用信息
- 这是以往C/C++ 都无法达到的
Option 枚举的使用 是为了解决 空值的带来的坏的影响
- match 是为了 匹配不同的枚举类型
_ 在match 匹配所有剩下的值 ,_ 在match 中是通配符号
在Rust 中 枚举必须穷举所有的枚举类型 (这是特别需要注意的点)
if let 减少代码量的匹配方法
let if_let = Some(0u32); //还可以进行数值的检测if let Some(3u8) = if_let {print!("good \n");}else{print!("Not good\n");}
- Option<T> 数据类型检测的优势就体现出来了
- 使用Option 时也需要注意数据类型必须一致的问题
Rust 中的 mod 使用,有点像 Class 那样组织数据
- pub 可以指定在mod 中 那些时公共拥有的
- use 关键字 和 as 关键字 C++ && python3 的用法
- 这样就可以在不同的作用域中进行使用
- 这样的引入方法就相当的nice 了
- 当然和python 中 一样 也支持相应的通配符号
struct 中 可以使用 new 来创建一个实例,有点像C++ 中的构造函数
接着, 我们 为 Guess 实现 了 一个 关联 函数 new, 用于 创建 新的 Guess 实例 ❷。 根据 这个 new 函数 的 定义
- trait 共有特性的使用问题,可以共所有的类型使用
- 这种特性,可以在内部,定义成不同的函数进行使用
trait 作为参数 和 语法糖的 使用 方式
需要使用代码进行熟悉 trait 的特性,String,str,vec!,hash_map 常用的和 泛型方法(周六日搞一搞)
******************************************************** [40%] 分割线
trait 特性
#[derive(Debug)]pub struct point<T,Q>{ //这样还是属于自动推导的类型,进行使用,比C++ 的模板方式要更加高效//不至于编译两次,用哪一个生成哪一个,和 Some 的用法是完全一致的pub mm: T,pub mn: Q}let mut test_point = point{ //主动告诉编译器的不同的类型选项mm: 5i32,mn: 23u32};print!("test point {:?}\n",test_point);//trait 用来定义为共享的行为,trait 是在生成库的 时候为大家定义的 行为//更类似于公共共享的接口pub trait Summary { fn summarize(& self) -> String; }pub struct NewsArticle { pub headline: String, pub location: String, pub author: String, pub content: String, }impl Summary for NewsArticle { fn summarize(& self) -> String { format!("{}, by {} ({})", self. headline, self. author, self. location) } }pub trait llp {fn llpf(&self) -> String; //这种用法是为了导入相应的结构体中,就是说这种用法没有}// 其中 trait 可以作为 一种 参数使用// 是一种 trait 约束的语法糖 的示例// pub fn llob<T:llp>(item: T) 实际上就是这种方式的语法,但是实现了一种语法糖的表现方式// pub 关键字也只是让私有变为共有,没有什么特殊的含义,oh yeach,最简单的权限实列pub fn llob(item: impl llp){ // 这是一个简单的实现 trait 的特性功能,而且可以作为参数使用item.llpf();}pub trait lloc {fn pppy(&self) -> String;}pub fn llod(ita: impl lloc + llp){print!("{} : {}\n",ita.llpf(),ita.pppy()); //可以同时使用两种不同的公共特性接口,}let test_circle= |x:u32| -> u32 { //这是rust 的闭包,闭包也可以作为一个参数使用x+1 };let gess = test_circle(10);print!("gess {}\n",gess); //还可以使用where 语法来解释,使用的trait 特性,减少代码的书写量fn some_debug<T,Q>(ita: T,itb: Q)->i32where T:lloc,Q:llp{32 }
这种方式,是为了使用trait 接口,对每个结构体都可以是使用这个公共接口,和C++ 的抽象接口,很像,也是形式上很想,意义和原理都不相同
这些是为了简化代码量
适配不同的情况,有适配器的思想和抽象接口的思想在里面
fn returns_summarizable() -> impl Summary{NewsArticle{ // 之前为结构体生已经绑定了一个方法,使用 Summary 的特性,对结构体返回,其实是对trait的返回headline: "Oh head".to_string(), //NewArticle 本来就是一个结构体location: "Now".to_string(),author: "en".to_string(),content: "no".to_string()}}let let_test = returns_summarizable();print!("let guess {}\n",let_test.summarize());
- 面向对象编程是为了多态
- 模块化编程也可以实现
- Rust 也是模块化编程,但是可以实现面向编程的功能,C 的话需要借助lds 连接器脚本 实现
- new,为自己分配,在Rust 的智能指针出,会有解释 因为整数中实现了 display 的方法,所以包含了tostring 的特性
- 可以将证书转化为 String 的字符串, String 集成了很多的方法
- 上面的意思是 当且仅当 Display 和 PartialOrd 中的 cmp 和 display 都实现了之后,才会实现 cmp_display( cmp && dispaly)
- 也就是说间接的实现了流程上的判断, 符合什么样的条件触发,cmp_display 的实现
- 这中方法,在代码达到一定的程度时,他的优越性就显现出来了
- 引用都会有生命周期, 一般情况下都是默认的,随着编译器的优化,对生命周期的标注会越来越少
标注生命周期是为了编译器计算变量的生命周期(了解就好)
- 终于看到了 static, 说的太多没用,就是为了 在全局使用, 和 C/C++ python 的globa 一个意思,
这个就和前面的 普通变量的赋值 扯上了关系
这个语言真他娘的刺激
当个人 不好吗? 简单点不好吗, 有必要,搞的比C++ 还复杂吗
Rust 的自动话测试
运行cargo test ,就会对代码模块进行测试看看有没有问题, 其他的,这个知道就行了
闭包的类型
******************************************************** [50%] 分割线
Rust 的 闭包 和 trait 也是重点
剩下的50% 学习点
- Rust 的高级特性 和 智能指针,就得慢慢来了,还包括安全用法和不安全用法的区别
- Rust 的 STL 库 的使用
- Web 服务器的实验项目 >> 这个是熟悉语言的最好方式
- 有哔哩哔哩的视频,我得去看一下,有助于理解
- Rust 中宏的定义和使用
需要声明引用的生命周期的案例
fn test_alive<'a>(x:&'a i32,y: &'a i32) -> &'a i32{ // 这里存在生命周期的不确定性,所以得显著的表明出来//这个周期的声明肯定是由 test_alive 这个函数开始的,以这个函数未结束//所以这个生命周期的生命也可以理解if x > y {x}else{y}
}
Rust & 就是指针
let ptr = &5; //活久见,直接指向了全局区的值,你他娘的,我大C的棺材盖压不住了
let ptr_test:&'static i32 = &7; //这是不是很刺激,其实是一样的道理,只不过省略了
trait trait 闭包 和 trait约束的
trait 的使用
pub trait Human{ //声明一个共有的特性,这个待定,定义公共的特性在里面
//也就是常见的C++ 抽象接口,但是意义不同
//C++ 可以通过抽象基类调用其子类,形成了抽象接口fn eat(&self) -> String;
}pub struct Woman{pub name:String,pub age:u32
}impl Human for Woman {fn eat(&self)->String{format!("{} eat apple\n",self.name)}
}fn main() {let lili = Woman{name:String::from("Lidali"),age:22,};print!("{}",lili.eat());
}
struct 的使用
pub trait Human{ //声明一个共有的特性,这个待定,定义公共的特性在里面fn eat(&self) -> String;
}pub struct Woman{pub name:String,pub age:u32,pub sleep_time:u32,
}impl Human for Woman {fn eat(&self)->String{ //共有特性format!("{} eat apple\n",self.name) }
}impl Woman{ //为我们增加单独的函数使用情况pub fn sleep(&self)-> bool{if self.sleep_time > 8{true}else{false}}
}fn test_trait(it: impl Human){ //trait 作为参数使用,怎么样就是抽象接口,实时证明trait 就是被用作抽象接口print!("human eat {}\n",it.eat());
}fn main() {let lili = Woman{name:String::from("Lidali"),age:22,sleep_time:9};print!("{}",lili.eat());print!("lili sleep st: {}\n",lili.sleep());test_trait(lili); //运行之后看打印,就知道怎么回事儿了
}
实时证明trait 就是被用作抽象接口, 之前的我理解错了, 在抽象接口的基础上理解,约束的问题,就容易了
trait 就是为抽象接口设计出来的
trait 就是为抽象接口设计出来的
trait 就是为抽象接口设计出来的
pub trait Human{ //声明一个共有的特性,这个待定,定义公共的特性在里面fn eat(&self) -> String;
}pub struct Woman{pub name:String,pub age:u32,pub sleep_time:u32,
}impl Human for Woman {fn eat(&self)->String{ //共有特性format!("{} eat apple\n",self.name) }
}impl Woman{ //为我们增加单独的函数使用情况pub fn sleep(&self)-> bool{if self.sleep_time > 8{true}else{false}}
}fn test_trait(it:& impl Human){ //trait 作为参数使用,怎么样就是抽象接口print!("human eat {}\n",it.eat());
}fn opt_trait(x: Woman) -> impl Human{ //返回对应的数据结构的特性方法x
}fn main() {let lili = Woman{name:String::from("Lidali"),age:22,sleep_time:9};print!("{}",lili.eat());print!("lili sleep st: {}\n",lili.sleep());test_trait(&lili); //所有权的问题得注意let trait_ues = opt_trait(lili);print!("trait opt {} \n",trait_ues.eat());
}
还是之前的代码,但是返回了,业务中,可以广泛的应用
有条件的实现 trait 约束
pub trait one_pub{fn echo(&self) -> String;
}pub trait two_pub{fn print(&self) -> String;
}struct trait_debug{name:String,
}impl one_pub for trait_debug{fn echo(&self) -> String{"one_pub".to_string()}
}impl two_pub for trait_debug{fn print(&self) -> String{"two_pub".to_string()}
}struct sercert<T>{doit:T,
}impl<T> sercert<T> {fn new(doit:T) -> Self {Self{doit}}
}impl<T:one_pub+two_pub> sercert<T>{fn todosomething(&self){print!("yeah I am here\n"); }
}fn main(){let pre = trait_debug{name:String::from("PRE"),};let one = sercert::new(pre);one.todosomething();
}
- 什么叫做有条件
- one: 同时有pub_one 和 pub_two 这两个特性的类型才行
- two: 如果没有,不好意思会直接报错,这就叫做有条件的 通过特性推导类型
- three: 例子少,但是还是能够说明这个问题
- 所以通过了特性,将数据类型 锁定到了一个范围之内,从而形成了约束, so, 书中给的例子,有说服力
- 应为 Display 是所有的 基础数据类型的 特性,u32 i32 i8 u8 。。。
- Partiaload 是所有比较大小的数据类型 u32 i32 i8 u8 。。。
- 拿着些就特别有说服力
这就是trait 的约束
再次理解生命周期
struct alive_str<'a>{part:&'a str,
}fn main(){let kacha = String::from("baidu.com");let first_kacha = kacha.split('.').next().expect("Error");let index = alive_str{part:first_kacha};print!("kacha first {}\n",index.part);
}
如果在结构体中 有引用,那么就得显示的说明,声明周期,为什么呢,因为你不知道,到底谁先挂掉,所以要告诉编译器,part 的生命周期和alive_str 一样,但是,只是标注出来,显示告诉编译器,但是无法改变生命周期
还有就是只有引用存在标注生命周期的需求
编译器 目前 使用 了 3 种 规则 来 计算 引用 的 生命 周期 (书中原话)
- 每一个 引用 参数 都会 拥有 自己的 生命 周期 参数。 换句话说, 单 参数 函数 拥有 一个 生命 周期 参数: fn foo<’ a>( x: &‘a i32); 双 参数 函数 拥有 两个 不同 的 生命 周期 参数: fn foo<’ a, 'b>( x: &'a i32, y: &'b i32); 以此类推
- 当 只 存在 一个 输入 生命 周期 参数 时, 这个 生命 周期 会被 赋予 给 所有 输出 生命 周期 参数, 例如 fn foo<’ a>( x: &'a i32) -> &'a i32。
- 当 拥有 多个 输入 生命 周期 参数, 而 其中 一个 是& self 或& mut self 时, self 的 生命 周期 会被 赋予 给 所有 的 输出 生命 周期 参数。 这条 规则 使 方法 更加 易于 阅读 和 编写, 因为 它 省略 了 一些 不必 要的符号
如果符合这三条就不需要显示的标注生命周期, 默认情况下符合以上,就不需要,标注,如果no,就需要标注,好的结束,nice
到这里就可以完全弄懂 trait 和 生命周期 的意图了
闭包和迭代器
struct CloseCircle<T>
where T:Fn(u32)->u32 //闭包的特性约束 FnMut(u32)->u32 FnOnce(u32)->u32 消耗捕获的变量
{func:T
}fn main(){let test = |num|{print!("num is {}\n",num); //这是默认返回的特性num + 10};let o = test(1);print!("o {}\n",o);let cankao = |x| x+10; //最简闭包最为使用的功能,显示的使用一个闭包并进行返回,也就是可以当作参数使用let cankao_v1 = cankao(10);print!("cankao_v1 {}\n",cankao_v1);let y = 100;let cankao_v2 = |x| x == y; //只能在闭包中使用捕获上下文的功能print!("cankao_v2: {}\n",cankao_v2(100));let close_circle = CloseCircle{func:|x:u32| -> u32 {x+120},};print!("close_circle test {}\n",(close_circle.func)(130));let v1 = vec![1,2,3,4];let mut iter = v1.iter();iter.next(); iter.next(); iter.next(); iter.next();print!("iter {:?}\n",iter);
}
获取枚举变量中的值
let test_enum = Some(&5);
print!("test enum {}\n",test_enum.unwrap());
- 由此可知这是由 枚举的 共有trait 获取 枚举中的值, 这个理念不错,我喜欢
- Rust 的编译器 有点烦 还管怎么 命名的,要不然 还告警 … …
- unwrap() 这个 trait 方法 记住意思very 重要,在以后的编程里面,就是相当nice的玩意
迭代器的消耗问题
let v1 = vec![1,2,3,4];let mut iter = v1.iter();iter.next();iter.next();iter.next();iter.next();print!("iter {:?}\n",iter);print!("v1 {} 的数组访问 {} \n",0,v1[1]); for it in iter{//所谓的消耗了迭代器,就是 指针的位置移动到了终点//按照C++,python 的情况下的理解就是这个样子,所以,书中的这个消耗//你是说得对,但是,不能这么玩呀print!("证明是否消耗了迭代器: {}\n",it);}
所谓的消耗了迭代器,就是 指针的位置移动到了终点
现在想一想这个特性的约束还是 十分的巧妙在里面
生成新的迭代器的方法
let v1 = vec![1,2,3,4];let mut iter = v1.iter();iter.next(); iter.next(); iter.next(); iter.next();print!("iter {:?}\n",iter);print!("v1 {} 的数组访问 {} \n",0,v1[1]); for it in iter{//所谓的消耗了迭代器,就是 指针的位置移动到了终点//按照C++,python 的情况下的理解就是这个样子,所以,书中的这个消耗//你是说得对,但是,不能这么玩呀print!("证明是否消耗了迭代器: {}\n",it);}let test_opt = Some(&5);print!("Test Opt {}\n",test_opt.unwrap());//迭代器是有惰性的,除非使用,否则没有任何的变化let v1_it:Vec<_> = v1.iter().map(|x| 2*x).collect(); //只会在迭代器移动是使用数值生成一个新的迭代器,但是不会对原有数据修改for t in v1{print!("update vec t {}\n",t);}
迭代器存在惰性
就需要使用,关键是collect 特性,促使迭代器动起来, 和 python 的 yield 的 关键字有异曲同工之妙
- iter 中的 filter 的特性 可以更具闭包 选择某些数据,然后在使用 collect 收集器起来,进行返回很实用
- 迭代器中返回的是Option Some(T),所以涉及到了enum 的解包问题
- 现在想想闭包和 trait ,这俩好棒呀,还有Option 这个枚举真的不错
- 道理上来讲,迭代器要比 loop while for 这些循环 要高效一些
自定义创建一个迭代器
#[derive(Debug)]
struct ItWonderful{cnt: u32,
}impl Iterator for ItWonderful{ //使用迭代器的特性type Item = u32;fn next(&mut self) -> Option<Self::Item>{self.cnt += 1;if self.cnt < 6 {Some(self.cnt)}else{None}}
}
// 对于一般的u32 i32 u64 i64 都有 copy 的特性fn main(){ let mut dabu = ItWonderful{cnt: 0,};for _it in 0..5{dabu.next(); // dabu 会发生变化,}print!("dabu {:?}\n",dabu);
}
这个next 方法就是相应的迭代器
还得养成一个new的习惯
struct ItWonderful{cnt: u32,
}impl ItWonderful {fn new() -> ItWonderful {ItWonderful{cnt:0}}
}
就是为了减少代码量
要记住字串符没有 Copy 的 特性,得clone 进行使用
再看 Copy 特性的问题
let _str = "hello"; //栈中的数据区域,无需回收 实际类型为 str 字符串切片
let _str_c = _str; //_str_c 获取到了_str 的所有权 "hello的" 其实是字符数组的 Copy 功能//常规赋值发生拷贝
print!("_str {}\n",_str);let _str = String::from("hello"); //在堆上开辟的字符串,在堆上的话就只有 浅拷贝 ,没有 Copy 特性
let _str_c = _str; // ---- once
let _str_d = _str; // FIXME: 是无法访问的
Box 智能指针的使用场景 (书中原话)
• 当你 拥有 一个 无法 在 编译 时 确定 大小 的 类型, 但又 想要 在 一个 要求 固定 尺寸 的 上下文 环境 中 使用 这个 类型 的 值 时。
• 当你 需要 传递 大量 数据 的 所有权, 但又 不 希望 产生 大量 数据 的 复制 行为 时。
• 当你 希望 拥有 一个 实现 了 指定 trait 的 类型 值, 但又 不关心 具体 的 类型 时
Deref trait 定义 引用的含义,自定义指针,快乐 ~^^~
use std::ops::Deref;struct auto_ptr<T>(T);//实现一个智能指针的方法 new
impl<T> auto_ptr<T> { fn new(x:T) -> auto_ptr<T> {auto_ptr(x)}
}//为自定义的智能指针添加
//解引用的方法
impl<T> Deref for auto_ptr<T>{ type Target = T;fn deref(&self) -> &T{&self.0}
}// 这是一个字符串切片的表面量
fn main(){ let int_ptr = auto_ptr::new(0u32);print!("init_ptr {}\n",*int_ptr);
}
书中关于: type Target = T 的解释
type Target = T; 语法 ❶ 定义 了 Deref trait 的 一个 关联 类型。 关联 类型 是一 种 稍微 有些 不同 的 泛 型 参数 定义 方式
慢慢来以后解释
Rust 函数默认 引用解引用的 隐式转换
Rust 会在 类型 与 trait 满足 下面 3 种 情形 时 执行 解 引用 转换:
• 当 T: Deref< Target= U> 时, 允许& T 转换 为& U。
• 当 T: DerefMut< Target= U> 时, 允许& mut T 转换 为& mut U。
• 当 T: Deref< Target= U> 时, 允许& mut T 转换 为& U。
******************************************************** [60%] 分割线
Rust 强大的告警功能
其实没错误只是,不符合他的命名规则
JetBrains 27 寸战神 名不虚传
补全效果比智能检测比 Vscode 好的多
Rust 元组和数组 的区别
- 元组不支持迭代,可以自己实现,官方也不建议创建迭代,因为 tuple 支持不同的数据类型,但是满足迭代器的条件,就是每次返回一个相同的 数据类型的,就算是使用Option<T> 这个T 的类型是唯一的,所以无法完成迭代
- 官方如果要使用迭代的话, 建议使用数组,可以在元组中使用多个数组,在对数组迭代,访问
元组和 数组的小用法
let test = (1, 2, 3, 4); //元组的的使用方法let (first, second, third, fourth) = test; //常见的元组解包使用let test = [1, 2, 3, 4];let [first, second, third, fourth] = test; //常见的数组解包let test = [1, 2, 3, 4, 5];let mut it = test.iter(); // 迭代器必须是变化的,换句话说是必须是移动的let it = it.next();print!("Some<T> {:?}\n", it);for it in test.iter() {// 使用 for 迭代 对 Option<T> Some(T) 解包print!("it {}\n", it);}
元组和数组的结合访问
fn print_array<'a, T>(x: &'a [T; 5], y: &'a str)
whereT: std::fmt::Display, //对参数追加trait 约束,为了正常使用{} ,格式化输出
{print!("array {}: ", y);for it in x.iter() {print!("{} ", *it);}print!("\n");
}fn main() {let array_one: [i32; 5] = [1, 2, 3, 4, 5];print_array(&array_one, "one");let array_two: [i32; 5] = [7, 8, 9, 0xa, 0xb];print_array(&array_two, "two");let tuple_mux = (&array_one, &array_two);print_array(tuple_mux.0, "tuple first");print_array(tuple_mux.1, "tuple two");
}
Result
### Rust 打印小方法, 和 C中的打印有些相同
let test = 0x1234;
print!("0x{:04x}\n",test);
一下截图来自通过例子学Rust
这是在以当前的作用域为有效的声明,和遮蔽有关系,这个这个官方,解释的很官方,加一个mut 一样是可以修改的
- 看下面所示的代码示例
fn main(){let mut _nums = 0x100;{let mut _nums = _nums;_nums = 0x200;print!("nums 0x{:03x}\n",_nums);}
}
Rust 中的显示转换
这就和 C 中的强制类型转换变的会一致
我感觉写出声明心里踏实点
as 关键字 可以作为 显示声明符合
这个和 typedef 相差不大 这个使用
From Into 特性 用作网络解包,可以整块解包,我还是喜欢C 的 ptr
主要是根据元组的顺序推导出来的
逐步可以解构访问的枚举方式
引用 解引用 解构
fn main() {let ref mut _ptr;let mut _value = 0x100;_ptr = &mut _value;print!("ptr dereference is 0x{:04x} \n", *_ptr);
}
这种声明就容易读
Rust 使用熟悉
#[allow(dead_code)]
enum Color<'a> {//双元的生命周期Red(&'a str, &'a i32),Black(&'a str, &'a i32),
}fn main() {#[allow(unused_imports)]use Color::{Black, Red};//对Color 进行初始化的示例使用let user_color = Red("red", &0x2);match user_color {Red("red", &0x2) => print!("yeach matched \n"),_ => print!("yeach nothing matched \n"),}
}
卫语句的使用也很使用
这种有利于 在枚举的时候 动态的获取数据类型
这种使用方法很普遍,尤其在逻辑越多的时候,越明显
// `age` 函数,返回一个 `u32` 值。
fn age() -> u32 {15
}fn main() {println!("Tell me type of person you are");// 这个绑定的语法相当好用match age() {n @ 1..=10 => println!("this switch is {}\n", n),n @ 11..=13 => println!("this switch is {}\n", n),n @ 14..=16 => println!("this switch is {}\n", n),_ => println!("default switch \n"),}
}
减少的逻辑代码很客观
循环标签的使用
fn main() {let mut cnt = 0;let pl = 'level_1: loop {cnt += 101;while cnt > 2 {if cnt == 100 {break 'level_1 cnt;}cnt -= 1;}};print!("pl value is {}\n", pl);
}
跳出第几层 的循环,不用在一层的一层的跳,咱们直接跳,这就很灵活了,实际开发中,经常会用到
官方中文学习手册
https://rustwiki.org/docs/
Move trait 的 特点
没有 move 的情况
编译器写的很清楚,无法获取 a 的 mutable attr
加上move 之后
move 的作用是将 强制 将 闭包 外的 所有权转移到 闭包内使用,否则默认的情况下,只能捕获借用,不能获取完整的所有权
和C++ 一样的析构函数,手动回收资源,用于自己创作的自定义类型
感冒头痛 … …
Some 解包小技巧
fn main() {let mut data = Some(0);let mut _use_data = || -> Option<i32> {//返回某些数值并使用? 解包,在Option 返回的闭包中data = Some(5);Some(data? + 10)};println!("data {} \n", data.unwrap());
}
年前最后一项技能树 Rust ,攻碉堡 ing (Bilibili 视频整理)相关推荐
- 【信息系统项目管理师】高项案例分析攻略
[信息系统项目管理师]高项案例分析攻略 摘要 高项的案例分析,都说只要做对计算题都稳过,这个结论不假,但也要其他有其他两道文字案例题分数得支撑.尤其在通过卡案例来维持低通过率的时候,这结论并不完全适用 ...
- 【干货】2021短视频营销攻略:短视频内容策略下的5i沟通法则.pdf(附下载链接)...
大家好,我是文文(微信号:sscbg2020),今天给大家分享阿里妈妈和淘宝短视频于2021年1月份联合发布的报告<2021短视频营销攻略:短视频内容策略下的5i沟通法则.pdf>,短视频 ...
- 视频号运营攻略大全,视频号从0到1运营变现教程丨国仁网络资讯
众所周知,近年来国内短视频行业蓬勃发展,抖音日活6亿,快手日活也达5亿多,腾讯一直眼馋着这块蛋糕,不仅投资了快手,还自己开发了短视频APP--微视. 可微视的发展不如人意.而视频号作为微信的" ...
- rust最美建筑_[资料整理]动物之森的美丽物语 (多图;补完)
Agent S 生日: July 2nd 性别: Female 口头禅: sidekick 个性: 活泼 种族: 花栗鼠 看图说话: "I'm gonna, like, save the w ...
- 微信视频号攻略:转发视频号的视频到自己的视频号上
新手如何转发视频号的视频到自己的视频号上,需要创作还是直接发呢? 我相信你开通视频号,都想赚钱但是真的你知道怎么赚钱吗? 视频号背靠微信,而且拥有最多的用户现在微信官方一直投入大量资源使劲推,视频号重 ...
- 多项新规重磅发布,微信视频号近期需要关注这几点
随着功能的完善和内容生态的丰富,视频号逐渐放慢产品更新频率,将重点放到商家准入标准.创作者扶持计划上来,本期我们将更侧重解读平台新规,帮助大家了解行业动向,把握最新趋势. 01 视频号小店结算规则修订 ...
- 直播攻略:网易视频云送上OBS直播完整版教程
OBS是什么? OBS是一款直播串流软件,中文无广告,完全免费,含32位与64位版本,通吃各种电脑,支持MAC的OS X系统. 各大平台都有自己的直播软件了还需要用OBS么? 因为,当前平台直播软件存 ...
- 短视频营销攻略:短视频内容策略下的5i沟通法则(2021.1).PDF
来源:阿里妈妈 公众号:参一江湖 以上是资料部分内容截图,更多海量精选内容 公众号:参一江湖 8月第一周资料总览(8.1-8.7) 2021年中国人工智能应用趋势报告. 2020-2021年中国短视频 ...
- 新增微信小程序和WebRTC连麦直播等多项能力,即构实时音视频SDK再升级!
经过2018年小半年的闭关练功,即构ZEGO团队铸造了不少黑科技.本文将为你带来即构ZEGO实时语音视频SDK近半年新增能力和功能优化的最新进展. 更懂应用场景的语音视频云 作为全球领先的实时语音视频 ...
最新文章
- Java 面试知识点解析(七)——Web篇
- 轻量级HTTP服务器Nginx(Nginx日常维护)
- Angular 9.0.0 版本已发布!
- Android 虚线分割Shape
- bzoj3601 一个人的数论 (拉格朗日插值求系数)
- 邮件系统IP被CBL列黑,怎么样里面申诉呢?
- 【机器人学】机器人运动学基础
- app移动接口开发需要注意什么
- 2018年深圳杯论文_2018.5.21/建模日记/深圳杯
- could not get batchedbridge, make sure your bundle is packaged correctly
- 教你怎么用手机进入路由器管理界面
- 百度风云榜实时热点API
- 飞秋官方下载 找了很久的
- UNITY个人版设置深色主题
- Android微信支付集成流程及其常见错误
- Asynchronous FIFO with gray code(异步FIFO verilog设计理念)
- 笔记本高分辨软件兼容问题,字体太小或模糊
- 活动详情页面html代码,折扣活动详情.html
- python工控开发框架_GitHub - hzglitter/isf: ISF(Industrial Security Framework),基于Python的工控漏洞利用框架...
- 图像运算和图像增强一