观看B站软件工艺师杨旭的rust教程学习记录,有删减有补充
枚举
枚举类型可以让我们的程序使用一些固定长度和固定数值的变量值范围
enum 枚举
枚举类型中的值被称为变体
enum IpAddrKind{V4,//变体V6,//变体
}
fn main(){let four = IpAddrKind::V4;let six = IpAddrKind::V6;route(four);route(six);route(IpAddrKind::V6);
}
fn route(ip_kind:IpAddrKind){
}
用struct
存储数据
enum IpAddrKind {V4, //变体V6, //变体
}
struct IpAddr {kind: IpAddrkind,address: String,
}
fn main() {let home = IpAddr {kind: IpAddrKind::V4,address: String::from("127.0.0.1"),};let loopback = IpAddr {kind: IpAddrKind::v6,address: String::from("::1"),};
}
将数据添加到变体
enum IpAddrKind{V4(u8,u8,u8,u8),V6(String),
}
fn main() {let home=IpAddrKind::V4(127.0.0.1);let loopback = IpAddrKind::V6(String::from("::1"));
}
嵌入其他类型
enum Message {Quit,Move { x: i32, y: i32 }, //匿名结构体Write(String),ChangeColor(i32, i32, i32),
}
fn main() {let q = Message::Quit;let m = Message::Move { x: 12, y: 12 };let w = Message::Write(String::from("Hello"));let c = Mssage::ChangeColor(0, 255, 255);
}
为枚举定义方法
enum Message {Quit,Move { x: i32, y: i32 }, //匿名结构体Write(String),ChangeColor(i32, i32, i32),
}
impl Message {fn call(&self) {println!("这是call方法"); //这是call方法}
}
fn main() {let q = Message::Quit;let m = Message::Move { x: 12, y: 12 };let w = Message::Write(String::from("Hello"));let c = Message::ChangeColor(0, 255, 255);m.call();
}
Option 枚举
Option
枚举类型来表示可能具有值或者可能没有值
- Rust 并没有空值,不过它拥有一个可以编码存在或不存在概念的枚举
- Rust强制您在使用可能为空的值时进行显式处理,以防止出现空值错误(Null Pointer Errors)或空值引起的其他问题
enum Option<T> {None,//包含在prelude中,可以直接使用Some(T),//包含在prelude中,可以直接使用
}
Some代表可能有值,可能没值
None代表没有值
fn main() {let some_string = Some(5);//`Option<i32>` 类型的变量let some_string = Some("A String");//`Option<&str>` 类型的变量let absent_number: Option<i32> = None;//`Option<i32>` 类型的空变量,变量不包含具体的值
}
- 要使用
Option<T>
中的T必须将它转换为T
fn main() {let maybe_number: Option<i32> = Some(42);// 转换 Option<i32> 到 i32let number: i32 = maybe_number.unwrap();//尝试从 `Some` 变体中提取值,并返回该值。如果`Option`是 `None` 变体,`unwrap()`方法会产生panic(崩溃)println!("{}", number);//42
}
标准库类型
Vector类型 动态数组
声明Vector
fn main() {let v1: Vec<i32> = Vec::new();//vec!是一个宏let v2 = vec![1, 2, 3];
}
访问元素
fn main() {let v = vec![1, 2, 3];println!("{:?}", v); //[1, 2, 3],vector没有实现display,所以使用格式化输出//索引访问println!("{}", &v[0]); //1//match get访问match v.get(2) {Some(third) => println!("{}", third), //3,有值就绑定到返回值third上None => println!("没有这个值"),}//for遍历for i in &v{println!("{}",i);//123}
}
添加删除元素
fn main() {let mut v = Vec::new();//添加元素v.push(1);v.push(2);v.push(3);v.push(4);//删除元素v.pop();println!("{:?}", v); //[1, 2, 3]
}
使用enum
在vector中存储不同类型的数据
#[derive(Debug)]
enum SpreadsheetCell {Int(i32),Folat(f64),Text(String),
}
fn main() {let row = vec![SpreadsheetCell::Int(3),SpreadsheetCell::Text(String::from("blue")),SpreadsheetCell::Folat(10.12),];//for遍历for i in &row {println!("{:#?}", i); //Int(3,)Text("blue",)Folat(10.12,)}
}
String类型 可变字符串
String
被存储为由byte
(字节)组成的 vector(Vec<u8>
),但是不允许索引访问!(UTF-8使用1到4个字节编码Unicode字符,索引访问无法确定字符的边界)- UTF-8编码
String
类型可获得所有权:OsString
、CString
&str
字符串切片对String
类型的借用:SsStr
、CStr
声明String
fn main() {let s1 = "初始化字符串"; //借用的切片let s2 = s1.to_string(); //String类型,由于切片借用不获得所有权,这里实际是&&s1.to_string()let s3 = "hello world s3".to_string(); //String类型let s4 = String::from("hello world s4"); //String类型println!("{},{},{},{}", s1, s2, s3, s4); //初始化字符串,初始化字符串,hello world s3,hello world s4
}
更新String
fn main() {let mut s1 = String::from("张");//将字符串切片附加到Strings1.push_str("三");println!("{}", s1); //张三//将单个字符附加到String,注意字符是''s1.push('🥹');println!("{}", s1); //张三🥹//拼接字符串let s2 = String::from("李四");//+的实现:pub fn add(self, rhs: Rhs) -> Self::Outputlet s3 = s1 + &s2; //add函数取得s1所有权并附加s2,然后将s1所有权move到s3,s1失效,s2是借用,不改变所有权println!("{}", s2); //张三🥹李四//format!宏会返回字符串let s4 = format!("{}-{}", s2, s3);println!("{}", s4); //李四-张三🥹李四
}
切割String
切片
fn main(){let s1 = "hello world";let s1 = &s1[1..4];println!("{}",s1);//hell
}
对于字符编码会不一致的字符串,rust无法推断字符长度,要谨慎操作,以下代码会发生panic,6
无法确定🐕
的边界
fn main(){let s1 = "hello🐕🦊";let s1 = &s1[0..6];println!("{}",s1);//hell
}
HashMap集合 哈希表
key
:value
存储数据
HashMap
不在prelude
中,需要use
导入HashMap
是同构的,所有的key
必须是同一类型,所有的value
必须是同一类型- 每个key只能对应一个
value
声明HashMap
use std::collections::HashMap;
fn main() {let mut scores: HashMap<String, i32> = HashMap::new();scores.insert(String::from("blue"), 10);scores.insert(String::from("blue"), 50);
}
访问
use std::collections::HashMap;
fn main() {let mut map: HashMap<String, i32> = HashMap::new();map.insert(String::from("blue"), 10);map.insert(String::from("red"), 50);let key = String::from("blue");let value = map.get(&key);match value{Some(s)=>println!("{}",s),//10_=>println!("不存在!"),}for(k,v)in &map{println!("{}:{}",k,v);//blue:10 red:50}
}
更新HashMap
use std::collections::HashMap;
fn main() {let mut map1: HashMap<String, i32> = HashMap::new();map1.insert(String::from("blue"), 10);map1.insert(String::from("blue"), 50);for (k, v) in &map1 {println!("{}:{}", k, v); //blue:50}println!("******************");//使用entry判断key是否存在,存在则不更新并返回对应的可变引用,不存在则更新并返回对应的可变应用let mut map2 = map1.clone();map2.entry(String::from("blue")).or_insert(40);map2.entry(String::from("red")).or_insert(60);for (k, v) in &map2 {println!("{}:{}", k, v); //red:60 blue:50}
}
例子
use std::collections::HashMap;
fn main() {let text = "hello world wonderful world";let mut map = HashMap::new();for word in text.split_whitespace() {//以空格分割let count = map.entry(word).or_insert(0); //存在则不更新并返回对应的可变引用,不存在则更新并返回对应的可变应用*count += 1; //解引用改变value的值}println!("{:?}", map); //{"wonderful": 1, "hello": 1, "world": 2}
}
泛型
提高代码复用能力,消除类型转换,不影响性能
fn largest<T: PartialOrd + Copy>(list: &[T]) -> T {let mut largest = list[0];for &item in list {if item > largest {largest = item;}}largest
}
fn main() {let number_list = [12, 4, 1, 5, 48];let result = largest(&number_list);println!("数组中最大的数字:{}", result);let char_list = vec!['a', 'S', 'w', 'r'];let result = largest(&char_list);println!("最大的字符:{}", result);
}
泛型结构体
struct Point<T, U> {x: T,y: U,
}
fn main() {let integer = Point { x: 5, y: 12.54 };let float = Point { x: 1.0, y: 4.0 };
}
泛型方法
struct Point<T, U> {x: T,y: U,
}
impl<T, U> Point<T, U> {fn mixup<V, W>(self, other: Point<V, W>) -> Point<T, W> {Point {x: self.x,y: other.y,}}
}
fn main() {let p1 = Point { x: 5, y: 4 };let p2 = Point {x: "Hello",y: "World",};let p3 = p1.mixup(p2);println!("混合两点的x、y(x:{},y:{})", p3.x, p3.y); //混合两点的x、y(x:5,y:World)
}
Associated Types 关联类型
在 trait 中定义类型占位符的机制,可以在实现 trait 时具体化
// 定义一个 Container trait,其中包含一个关联类型 Item
trait Container {type Item;//占位符,用于表示容器中元素的类型fn first(&self) -> Option<&Self::Item>;// 获取容器中的第一个元素的引用fn last(&self) -> Option<&Self::Item>;// 获取容器中的最后一个元素的引用
}
// 对 Vec<T> 实现 Container trait
impl<T> Container for Vec<T> {type Item = T;//将 Item 关联类型具体化为 T,即 Vec 中元素的类型fn first(&self) -> Option<&Self::Item> {self.get(0)}fn last(&self) -> Option<&Self::Item> {self.get(self.len() - 1)}
}
// 对自定义的容器 MyContainer 实现 Container trait
struct MyContainer<T> {elements: Vec<T>,
}
impl<T> Container for MyContainer<T> {type Item = T;//将 Item 关联类型具体化为 Tfn first(&self) -> Option<&Self::Item> {self.elements.first()}fn last(&self) -> Option<&Self::Item> {self.elements.last()}
}
fn main() {let v = vec![1, 2, 3, 4];let my_container = MyContainer {elements: vec![10, 20, 30, 40],};// 使用 Vec<T> 实现的 Container traitprintln!("vec第一个元素: {:?}", v.first()); //vec第一个元素: Some(1)println!("vec最后一个元素: {:?}", v.last()); //vec最后一个元素: Some(4)// 使用 MyContainer 实现的 Container traitprintln!("MyContainer第一个元素: {:?}", my_container.first()); //MyContainer第一个元素: Some(10)println!("MyContainer最后一个元素: {:?}", my_container.last()); //MyContainer最后一个元素: Some(40)
}