常用的集合
数组和tuple是存在栈上的,这里写的集合是存储在heap上的,也就是说在运行是存储大小是不定的。
Vector 不定长数组
类型签名: Vec
由标准库提供,可存储多个值,只能存储相同类型的数据,值在内存中连续存放。
创建Vector
当想创建一个不定长的数组时,可以使用Vector。
Vec::new();let c: Vec<i32> = Vec::new();
使用初始值创建Vec,使用 vec! 宏。let c = vec![1, 2, 3];
更新Vector
使用push。
fn main(){
let mut v = Vec::new();
// 添加元素后,rust编译就推断出类型,就不需要显示的指明类型了。
v.push(1)
}
删除 Vector
与其他的struct一样,当Vector离开作用域之后它就被清理掉了,所有的元素也被清理掉了。
读取 Vector中的值
索引和get方法。
fn main(){
let mut v = vec![1, 2, 3, 4, 5];
let one: &i32 = &v[0];
match v.get(0) {
Some(one) => one,
None => print!(),
}
}
用下标的方式超出索引rust会恐慌,match则会匹配到None。
所有权和借用规则。
不能在同一作用域内同时拥有可变和不可变的引用。
fn main(){
let mut v = vec![1, 2, 3, 4, 5];
let one = &mut v[0];
// rust 这里会报错,原因为不能借用多次v为可变的。v和one现在都是可变的,所以报错。
v.push(1);
print!("{}\n", one)
}
fn main(){
let mut v = vec![1, 2, 3, 4, 5];
let one = &v[0];
// rust 这里会报错,原因为不能即是可变的又是不变的。
v.push(1);
print!("{}\n", one)
}
借用规则防止的是当一个在heap上的数据发生内存地址改变后,引用仍指向原来的地址。此时就会有错。
遍历里面的值
for i in &c {}
Vector + enum
因为Vector里要求类型都是一样的。所以可以使用枚举,在枚举中定义参数接收不同类型,以达到Vector里放不同类型的数据的目的。
enum Money {
CNY,
JPY(u16),
USD(Bank),
}
#[derive(Debug)]
enum Bank {
HSBC,
BOC,
}
fn main() {
let gotoCountry = vec![
Money::CNY,
Money::JPY(1000),
Money::USD(Bank::HSBC),
];
}
String
String默认用utf-8。
rust在核心语言层面,只有一个字符串类型:字符串切片str(&str)。
字符串切片
对存储在其他地方,UTF-8编发的字符串引用。字符串字面值存储在二进制文件中,也是字符串切片。
String类型和&str是两个东西。
String类型
来自标准库,而不是核心语言。是一个更高级的类型。
创建新的字符串
String::new();let mut s = String::new();
new()方法是不能直接初始化值。
to_string();let s = "hello".to_String();
String::from();let s = String::from("hello.");
更新String
push_str(); 把一个字符串切片附加到String里。
let mut s = String::from("hello.");
s.push("World");
push(); 把单个字符附加到String里。
- 拼接,要求前面是String类型,后面是String引用。
let s = s1 + &s2
拼接后s1不能使用了。
format!();format("{}{}{}", a, b, c)
类似python。但不会取得所有权。
String 索引
rust字符串不支持索引语法访问。
len();let s = String::from("dd").len();
因为String底层是一个 vec 的不定长列表,里面装的是unicode字节值,所以杜绝了这种方法。
另外,所以操作的复杂度是O(1),String无法保证长度,所以需要遍历所有内容。
字节,标量值和字形簇
bytes 字节, chars 标量值, ...
切割String
let s = &hello[0..4];
必须沿着字符的边界切割。
HashMap
HashMap<k, v> 键值对,类似字典。数据是存在heap上,hashmap是同构的,所有的key是一种类型,所有的value是一种类型。
创建hashmap
创建空 HashMap: new();
添加数据:insert();
let mut account: HashMap<String, u16> = HashMap::new();
let mut account = HashMap::new();
account.insert(String::from("achu"), 123);
需要手动引入use std::collections::HashMap;
collect
在元素类型为tuple的Vector上使用collect方法可以组建一个hashmap。本质是Vector。
collect方法可以把数据整合成很多种集合类型,包括 hashMap。
合并两个vec成为hashmap
let teams = vec!["blue".to_string(), "red".to_string()];
let scores = vec![10, 50];
let res: HashMap<_, _> = teams.iter().zip(scores.iter()).collect();
println!("res = {:#?}", res);
遍历hashMap
推荐:使用for (i, k) in &hashmapE {}
.get()方法配合match去匹配返回的option枚举使用。
HashMap 所有权
- 对于实现了copy trait的类型(eg: i32),值会被赋值到hashmap中。
- 拥有所有权的值,值会被移动,所有权会被转移给hashmap,如果将值的引入插入到hashmap里,值本身不会移动。
use std::collections::HashMap;
fn main() {
let username = String::from("achu");
let password = String::from("1234");
let mut account = HashMap::new();
// 此时的两个变量的所有权移交给了hashmap
account.insert(username, password);
// 这里再调用就会报错
println!("{}{}", username, password)
}
更新hashmap
hashmap大小可变,每个k只能对应一个v。
替换
插入一个同样的key,不同的value就会被替换。
不存在key再插入
entry(k);,返回枚举,查询key是否存在。
or_insert(5),key存在,返回到对应的V的一个可变引用。key不存在,就将方法参数作为k的新值插进去,返回这个值的可变引用。
use std::collections::HashMap;
fn main() {
let text = "hello world";
let mut map = HashMap::new();
for i in text.split_ascii_whitespace() {
// 这里是对hashmap传值,如果有就返回这个value的引用,没有就设置为0然后返回value的引用
let count = map.entry(i).or_insert(0);
// 这里要解引用
*count += 1;
}
print!("{:#?}", map)
}