五. 结构体
5.1 结构体定义
struct xxx{}
//定义结构体
#[derive(Debug)]
struct User{
name:String,
age:u32,
active:bool,
}
//元组结构体
#[derive(Debug)]
struct Color(i32, i32, i32);
//空结构体
struct AlwaysEqual;
fn main(){
let mut user = User{//实例
name:String::from("john"),
age:20,
active:true,
};
user.name = String::from("jane");
user.age = 21;
println!("{:?}",user);
//简写语法
let name = String::from("jane");
let age = 21;
let user = User{
name,
age,
active:true,
};
println!("{:?}", user);
//简写语法2
let user2 = User{
active:true,
..user
};
println!("{:?}", user2, );
//
let c = Color(1,2,3);
println!("{:?}",c);
}
5.2 结构体使用
#[derive(Debug)]
struct Demo {
height: u32,
width: u32,
}
fn main() {
let d = Demo {
height: 10,
width: 10,
};
println!("{:?}", area(&d));//100
}
fn area(d: &Demo) -> u32 {
d.height * d.width
}
5.3 方法语法
impl.
所有在 impl
块中定义的函数被称为关联函数;
每个结构体都允许拥有多个 impl
块;
#[derive(Debug)]
struct Demo {
height: u32,
width: u32,
}
impl Demo {
fn area(&self)->u32{
self.height * self.width
}
}
fn main() {
let d = Demo {
height: 10,
width: 10,
};
println!("{:?}", area(&d));//100
println!("{:?}", d.area());//100
}
fn area(d: &Demo) -> u32 {
d.height * d.width
}
六. 枚举和模式匹配
6.1 定义枚举
enum IpAddr {
IpV4,
IpV6,
}
//定义枚举
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
fn main() {
let home = IpAddr::IpV4;
match home {
IpAddr::IpV4 => println!("home is ipv4"),
IpAddr::IpV6 => println!("home is ipv6"),
}
}
6.2 Option 枚举
Option
是标准库定义的另一个枚举;
#![allow(unused)]
fn main() {
enum Option<T> {
Some(T),
None,
}
}
Rust 并没有空值,不过它确实拥有一个可以编码存在或不存在概念的枚举。这个枚举是 Option<T>
6.3 match 匹配
fn main() {
let dice_roll = 9;
match dice_roll {
3 => add_fancy_hat(),
7 => remove_fancy_hat(),
_ => (),//通配模式和 _ 占位符
}
fn add_fancy_hat() {}
fn remove_fancy_hat() {}
}
if let
获取通过等号分隔的一个模式和一个表达式。它的工作方式与 match
相同,这里的表达式对应 match
而模式则对应第一个分支。
七. 包管理
一个包可以包含多个二进制 crate 项和一个可选的 crate 库
- 包(Packages): Cargo 的一个功能,它允许你构建、测试和分享 crate。
- Crates :一个模块的树形结构,它形成了库或二进制项目。
- 模块(Modules)和 use: 允许你控制作用域和路径的私有性。
- 路径(path):一个命名例如结构体、函数或模块等项的方式
7.1 包和crate
一个包中至多 只能 包含一个库 crate(library crate);包中可以包含任意多个二进制 crate(binary crate);包中至少包含一个 crate,无论是库的还是二进制的。
我们用关键字 mod
定义一个模块,指定模块的名字,并用大括号包围模块的主体。
路径有两种形式:
绝对路径(absolute path)从 crate 根部开始,以 crate 名或者字面量 crate
开头。
相对路径(relative path)从当前模块开始,以 self
、super
或当前模块的标识符开头。
pub 对外暴露/公用;
使用super 起始的相对路径;
pub fn add_fancy_hat(){}
mod front_of_house{
pub mod hosting{
pub fn eat_at_restaurant(){
println!("Eat at home");
}
}
fn _greeting(){
println!("Hello from the front of house");
//super
super::add_fancy_hat();
}
}
fn main(){
//绝对路径
crate::front_of_house::hosting::eat_at_restaurant();
//相对路径
front_of_house::hosting::eat_at_restaurant();
}
use 引入; as 重命名
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {
println!("add to waitlist");
}
}
}
//use
use crate::front_of_house::hosting as host;
pub fn eat_at_restaurant() {
host::add_to_waitlist();
}
使用pub use 导出重用
引入外部包;
[dependencies]
rand = "0.8.3"
使用{} 引入多个,使用* 引入所有;
文件分割 mod name/fn name
八. 数据结构集合
集合指向的数据是储存在堆上的,这意味着数据的数量不必在编译时就已知,并且还可以随着程序的运行增长或缩小。每种集合都有着不同功能和成本;rust 常见的3种集合实现;
vector 允许我们一个挨着一个地储存一系列数量可变的值
字符串(string)是字符的集合。我们之前见过 String
类型;
哈希 map(hash map)允许我们将值与一个特定的键(key)相关联。这是一个叫做 map 的更通用的数据结构的特定实现。
Rust 的集合可以分为四个主要类别:
- 序列: Vec、VecDeque、LinkedList
- Maps: HashMap, BTreeMap
- 集合: HashSet、BTreeSet
- 混杂: BinaryHeap
8.1 vector
fn main() {
//new vec
let mut v = Vec::new();
v.push(1);
v.push(2);
println!("{:?}", v);
let _b = vec![1,2,4];
}
8.2 String
fn main() {
//new string
let v = String::new();
//to string
let data = "dddd";
let s = data.to_string(); //String::from(self)
//push_str
let mut s = String::from("hello");
s.push_str(" world"); //
s.push('!');//push
println!("{}", s); //hello world!
}
8.3 哈希map
fn main() {
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);
println!("{:?}", scores); //{"Yellow": 50, "Blue": 10}
let teams = vec![String::from("Blue"), String::from("Yellow")];
let initial_scores = vec![10, 50];
let scores: HashMap<_, _> = teams.iter().zip(initial_scores.iter()).collect();
println!("{:?}", scores) /*{"Blue": 10, "Yellow": 50}*/;
//获取值
println!("{:?}", scores.get(&String::from("Blue")));
//更新
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Blue"), 20);
//没有值时插入
scores.entry(String::from("Blue")).or_insert(50);
println!("{:?}", scores);
}
九. 错误处理
rust 错误分为可恢复(Result<T, E>)和不可恢复的(panic!);
9.1 不可恢复painc
当出现 panic 时,程序默认会开始 展开(unwinding),这意味着 Rust 会回溯栈并清理它遇到的每一个函数的数据,不过这个回溯并清理的过程有很多工作。另一种选择是直接 终止(abort),这会不清理数据就退出程序。那么程序所使用的内存需要由操作系统来清理
[profile.release]
panic = 'abort'
9.2 可恢复Result!
use std::{fs::File, io::ErrorKind};
fn main() {
let f: Result<File, std::io::Error> = File::open("hello.txt");
let _f = match f {
Ok(file) => file,
Err(error) => match error.kind() {
ErrorKind::NotFound => match File::create("hello.txt") {
Ok(fc) => fc,
Err(e) => panic!("Problem creating the file: {:?}", e),
},
other_error => panic!("Problem opening the file: {:?}", other_error),
},
};
}
传播错误简介:?运算符 ;错误返回传递->Result<T,E>
十. T/trait/生命周期
泛型是具体类型或其他属性的抽象替代;
trait这是一个定义泛型行为的方法;
生命周期(lifetimes)Rust 的生命周期功能允许在很多场景下借用值的同时仍然使编译器能够检查这些引用的有效性。
10.1 范型
Rust 通过在编译时进行泛型代码的 单态化(monomorphization)来保证效率。单态化是一个通过填充编译时使用的具体类型,将通用代码转换为特定代码的过程。
10.2 trait
trait 告诉 Rust 编译器某个特定类型拥有可能与其他类型共享的功能。可以通过 trait 以一种抽象的方式定义共享的行为。可以使用 trait bounds 指定泛型是任何拥有特定行为的类型。
#![allow(unused)]
fn main() {
pub trait Summary {
fn summarize(&self) -> String {
String::from("(Read more...)")
}
}
}
//trait bound
pub fn notify<T: Summary>(item: T) {
println!("Breaking news! {}", item.summarize());
}
//多个 trait
pub fn notify(item: impl Summary + Display) {
//T
pub fn notify<T: Summary + Display>(item: T) {
//where
fn some_function<T, U>(t: T, u: U) -> i32
where T: Display + Clone,
U: Clone + Debug
{
}
10.3 生命周期
'xxxx 代表/ 'static 代表静态的生命周期;
十一. 自动化测试
让我们看看 Rust 提供的专门用来编写测试的功能:test
属性、一些宏和 should_panic
属性。
11.1 测试编写
fn main() {}
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}
使用should_panic 检测异常
fn main() {}
pub struct Guess {
value: i32,
}
// --snip--
impl Guess {
pub fn new(value: i32) -> Guess {
if value < 1 {
panic!("Guess value must be greater than or equal to 1, got {}.",
value);
} else if value > 100 {
panic!("Guess value must be less than or equal to 100, got {}.",
value);
}
Guess {
value
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[should_panic(expected = "Guess value must be less than or equal to 100")]
fn greater_than_100() {
Guess::new(200);
}
}
Result 测试
#![allow(unused)]
fn main() {
#[cfg(test)]
mod tests {
#[test]
fn it_works() -> Result<(), String> {
if 2 + 2 == 4 {
Ok(())
} else {
Err(String::from("two plus two does not equal four"))
}
}
}
}
11.2 测试运行
cargo test -- --test-threads=1//并行
cargo test -- --show-output//输出
cargo test one_hundred//制定名字
cargo test -- --ignored //忽略 #[ignore]