0.使用rustup update来升级rust
1.通常使用cargo来构建项目
1)cargo build用来构建代码,cargo check用来检查代码能否通过编译
2)cargo run用来构建并运行
3)cargo构建的target放在target/debug目录下,cargo build --release在优化模式下构建代码,生成的文件放在target/release目录下
4)cargo update可以强制更新依赖包,这个指令会忽略Cargo.lock文件,后者是为了确保构建是可重现的。
5)cargo doc --open可以在本地构建一份有关所有依赖的文档,但是没有出现以来库的文档?
2.crate代表了一系列源代码文件的集合
1)分为二进制包(binary crate)和库包(library crate)
3.为了保证安全的写出复杂甚至并行得代码,Rust默认变量是不可变的。
1)变量和常量的区别在于,变量用let关键字声明,常量用const关键字声明,同时声明时必须显示的标注值得类型,如const MAXPOINTS: u32 = 100000;。
常量可以声明在全局作用域中,只能将常量绑定到一个常量表达式上,而无法将一个函数得返回值,或者其他需要在运行时计算的值绑定到常量上。
2)连续使用let关键字可以隐藏前面的变量,它不同于mut,前者可以保持变量的不可变性。使用let隐藏也可以达到使用相同变量名代表不同类型值得用途。
3)整数字面量89222(Decimal),0xff(Hex),0o77(Octal),0b11110000(Binary),b'A'(Byte)
4)char类型占用四个字节,是Unicode标量值。
5)数据类型分为标量类型和符合类型。标量类型就是内置得数据类型,复合类型可以将多个不同类型得值组合为一个类型。Rust提供两种复合类型,元组和数组。
4.函数得参数必须显示声明类型
1)函数体中可以使用语句或者表达式,语句指的是那些执行操作但是没有返回值得指令,而表达式是会进行计算并产生一个值作为结果得指令。
2)let y = 6;是一条语句,x + y是表达式(注意没有分号)。调用函数,调用宏,花括号{}都是表达式。
3)函数如果只有语句,没有表达式返回,会默认返回一个空元组()
5.控制流
1)if后面接得表达式必须产生一个bool类型得值,if else else if
2)Rust提供三种循环 loop while for。loop可以返回值,如break 2;会返回2。while用于条件循环,for用于遍历数据。
3)(1..4)表示[1,2,3,4],rev()表示倒叙。iter()表示迭代。
6.Rust的所有权 !!!!
1)栈中的数据必须拥有一个已知且固定的大小,那些在编译期无法确定大小的数据,只能存储在堆中。
2)所有权规则:1.Rust中的每一个值都有一个对应的变量作为它的所有者。2.在同一时间内,值有且仅有一个所有者。3.当所有者离开自己的作用域时,它持有
的值就会被释放掉。引用是没有所有权的。
3)存在栈上的数据不需要所有权,存在堆上的数据才需要所有权。
4)Rust使用移动(move)来代替浅拷贝与深拷贝,因为移动会使之前的变量变为无效!Rust永远不会自动地创建数据的深拷贝。
5)如果一个类型实现了Copy trait,那么这种类型在给其他变量赋值后可以保证可用性,不用移动。如果一个类型实现了Drop trait,那么Rust就不允许这个
类型再实现Copy trait。默认拥有Copy trait的类型一般有整形,bool,char,浮点型,包含以上的元组。
6)将值传递给函数在语义上类似于对变量进行赋值。
7)引用允许在不获取所有权的前提下使用值。引用&的相反操作叫做解引用。
8)为了避免数据竞争data race,可变引用只允许在特定作用域中声明一个。不能在拥有不可变引用的同时创建可变引用。同时存在多个不可变引用则是合理的。
总结就是,在任何一段给定的时间里,要么只有一个可变引用,要么有多个不可变引用。引用总是有效的。
9)切片也不会获取数据所有权。字符串类型为String,字符串切片的类型为&str。数组也同样可以使用切片。
7.结构体 !!!!
1)结构体与元组相似,区别在于结构体中每个数据必须要有名字,而不再需要向元组一样通过索引来访问数据。
2)元组结构体可以省略字段的名字,如struct color(i32, i32, i32).
3)print打印不了自定义结构体,需要实现Display trait。
4)关联函数是不接收self的方法。
5)可以使用多个impl块。
8.枚举类型 !!!!
1)枚举可以关联相应的数据
2)直接将数据附加到枚举的每个变体中,就可以不额外使用结构体了。每个变体可以拥有不同类型和数量的关联数据。
3)个人理解,有点儿基类的意思,但是与box的区别是什么呢???
4)枚举也可以impl
5)match表达式是用来处理枚举的控制流结构。整个match表达式有返回值。match匹配必须穷举所有的可能!当不需要处理所有可能的值时,使用_通配符来代替其余的值。
6)在只关心一种值的情况下,可以使用if let简单控制流。if let可以搭配else。
9.管理项目,包,单元包及模块
1)一个包可以包含任意多二进制单元包和最多一个库单元包。src/main.rs为二进制单元包,src/lib.rs为库单元包。如果想要多个二进制单元包,需要在src/bin下创建来
添加多个二进制单元包。
2)模块决定了一个条目是公有的还是私有的。以mod关键字开头来定义一个模块,模块内可以继续定义模块.Rust开发者倾向于使用绝对路径来引用模块。
3)Rust中的所有条目默认都是私有的。(条目包括结构体,函数,方法,枚举,常量和模块)。父模块默认无法使用子模块的私有条目,但是子模块可以使用所有祖先模块中
的条目。
4)可以使用super来指代父模块。
5)struct设置为公有后,字段也需要设置公有才能对外使用。enum设置为公有后,所有变体都为公有。
6)使用use关键字可以将路径path引入作用域,简化调用。使用相对路径引入时必须使用self开始,但是不建议使用!
7)使用as关键字来提供新的名称,use std::io::Result as IoResult,可以避免同名冲突问题。
8)使用pub use关键字可以将名称重导出,可以将use的路径在模块中使用,也可以被外部代码使用。pub mod可以将嵌套的模块导出!
9)使用嵌套路径可以清理众多use语句,如use std::{cmp::Ordering, io};use std::io::{self, Write};
10)可以将模块拆分成不同的文件,在mod front_of house后使用分号而不是代码块会让Rust前往与当前模块同名的文件中加载模块内容!
10.通用集合类型
1)使用vec想要存储不同类型的数据,可以使用enum枚举,再通过match匹配枚举中的值。如果无法穷举所有枚举,就要使用动态trait类型。
2)使用format!可以格式化字符串,format!("{}-{}-{}", s1, s2, s3);这不会获取任何参数的所有权。
3)字符串不可以通过索引来获取字符,因为Rust中字符是按照UTF-8存的。但是可以使用&s[0..4]这种范围来获取字符,但是当获取失败时会panic。
通常的取字符串的方法是,使用chars方法,或者bytes方法。chars返回字符,bytes返回原始字节!
4)HashMap的insert会覆盖之前的值,可以使用entry.or_insert来判断是否有值并且在没有值的时候插入。or_insert和entry为我们返回一个&mut v,这样就可以
修改值了!需要使用解引用才能修改!
5)哈希函数可以抵御拒绝服务攻击DDOS,哈希函数为了安全性付出了一些性能代价。可以通过实现BuildHasher trait来修改哈希函数。
11.错误处理
1)Rust将错误分成两类:可恢复错误和不可恢复错误。可恢复错误例如文件未找到,不可恢复错误例如Bug。可恢复错误类型未Result
}
5)返回值也可以返回trait类型,类似参数约束的语法糖。如: fn returns() -> impl Summary{};但是这种写法无法返回可能会有多个类型的返回值!
6)trait约束可以有条件的实现方法。如:impl<T: Display + PartialOrd>Pair<T>{},也可以为实现了某个trait的类型有条件的实现另一个trait,这叫做
覆盖实现,如:impl<T: Display> ToString for T{},这会使任何实现了Display trait的类型都实现了ToString trait,从而可以使用to_string()方法!
7)Rust的每个引用都有自己的生命周期,它对应着引用保持有效性的作用域。&i32, &'a i32, &'a mut i32.标注生命周期是为了向Rust描述多个泛型生命周期
参数之间的关系!比如有两个参数的函数,参数1的生命周期和参数2的生命周期都是'a,意味着参数1和参数2的引用必须与这里的泛型生命周期存活一样长的时间,
函数返回的引用的生命周期与传入的引用的生命周期中较短的那一个相同。
8)生命周期语法就是用来关联一个函数中不同参数及返回值的生命周期的。一旦它们形成了某种联系,Rust就获得了足够的信息来支持保障内存安全的操作,并
阻止那些可能会导致悬垂指针或其他违反内存安全的行为!
9)可以在结构体中存储引用,但是需要为结构体定义中的每一个引用都添加生命周期标注。
10)生命周期三条省略规则:1.为每个输入参数生成一个生命周期。2.当只有一个输入,输出生命周期与输入生命周期相同。3.若有多个输入生命周期参数,而其中
一个是&self或&mut self,将self生命周期赋给输出。若三个步骤后所有引用都有生命周期了,则改方法或函数可以使用省略规则。
11)所有的字符串字面量都拥有'static 静态生命周期,表示整个程序的执行期。如:let s: &'static str = "i have a static lifetime!"。
13.自动化测试
1)虽然测试可以高效地暴露程序中的Bug,但是在证明Bug不存在方面却无能为力。使用自动化测试来尽量防止错误。
2)Rust测试就是一个标注又test属性的函数!属性(Attribute)是一种用于修饰Rust代码的元数据。只需要将#[test]添加到关键字fn的上一行便可以将函数
变为测试函数。
3)编写完测试函数,可以使用cargo test命令来运行测试。这个命令会构建并执行一个用于测试的可执行文件,该文件在执行的过程中会逐一调用所有标注了test
属性的函数,并生成统计测试运行成功或失败的相关报告。cargo test生成的二进制文件默认会并行执行所有的测试,并截获测试运行过程中产生的输出来让测试
结果相关的内容更加易读。
4)shouldpanic属性,标记了这个属性的函数会在代码发生panic时顺利通过,而在代码不发生panic时执行失败。在shouldpanic中添加expected参数,可以
让测试更加精细一些,它会检查panic发生时输出的错误提示信息是否包含了expected中包含的文字。
5)可以通过返回Result