Skip to main content

Vector

使用Vector储存多个值

  • Vec<T>,叫做vector
    • 由标准库提供
    • 可以储存多个值
    • 所有值的类型相同
    • 值在内存中连续存放

创建一个vector

let  v: Vec<i32> = Vec::new();
  • 使用初始值创建Vec<T>,使用vec!
let v = vec![1, 2, 3, 4, 5];
  • 更新vector
let mut v = Vec::new();
v.push(1);
v.push(2);
v.push(3);
  • 与其他类型一样,当vector离开作用域时,vector和其所有元素都被丢弃

  • Vector 根据index查找元素

let v = vec![1, 2, 3, 4, 5];
let third = &v[2];
println!("The third element is {}", third);

match v.get(2){
Some(third) => println!("The third element is {}", third),
None => println!("There is no third element."),
}

第一种在索引不存在时会引发panic,第二种会返回None

match v.get(100){
Some(third) => println!("The third element is {}", third),
None => println!("There is no third element."),
}

返回:There is no third element.

所有权和借用规则

  • 不能在同一作用域中同时存在可变和不可变引用 这里编译器就会报错:
let v = vec![1, 2, 3, 4, 5];
let first = &v[0];
v.push(6);
// error: cannot borrow `v` as mutable because it is also borrowed as immutable
println!("The first element is: {}", first);

遍历vector

let v = vec![1, 2, 3, 4, 5];
for i in &v {
println!("{}", i);
}
let mut v = vec![1, 2, 3, 4, 5];
for i in &mut v {
*i += 50;
}
for i in &v {
println!("{}", i);
}

使用枚举储存多种类型

  • Enum 的变体可以附加不同类型的数据
  • Enum 的定义在同一个enum下
enum SpreadsheetCell {
Int(i32),
Float(f64),
Text(String),
}

fn main() {
let row = vec![
SpreadsheetCell::Int(3),
SpreadsheetCell::Float(10.12),
SpreadsheetCell::Text(String::from("Hello")),
];

}

String

String 的数据结构复杂

  • Rust 的核心语言层面,只有一个字符串类型:字符串切片str&str

  • 字符串切片: 对储存在其他地方, UTF-8编码的字符串的引用

    • 字符串字面值: 储存在二进制文件中, 也就是字符串切片
  • String 类型:

    • 来自标准库, 可增长, 可变, 有所有权, UTF-8编码

创建一个新的字符串

  • 很多Vec<T>的操作都可用于String

  • String::new() 创建一个空的字符串

  • 使用to_string()方法, 可用于实现了Display trait的类型, 包括字符串字面值

let data = "initial contents";
let s = data.to_string(); // convert string literal to String
let s1 = "initial contents".to_string(); // same as above
  • 使用String::from()方法, 从字面值创建String
let s = String::from("hello");

更新字符串

  • 使用push_str()方法, 将字符串切片附加到String
let mut s = String::from("foo");
s.push_str("bar");
println!("{}", s);
  • 使用push()方法, 将单个字符附加到String
let mut s = String::from("hello");
s.push(';'); // push a char
println!("{}", s);
  • 使用+运算符或拼接字符串
    • 使用了类似这个签名的方法:fn add(self, s: &str) -> String{...}
    • +运算符会取得其第一个参数的所有权, 并且使第一个字符串不可用
    • 标准库中的add使用了泛型
    • 只能把&str拼接到String上
    • 解引用强制转换&String&str
let s1 = String::from("hello");
let s2 = String::from("world");
let s3 = s1 + &s2; // note s1 has been moved here and can no longer be used
println!("s3 = {}", s3);
println!("s2 = {}", s2);
// println!("s1 = {}", s1); // error: value borrowed here after move
let s1 = String::from("tic");
let s2 = String::from("tac");
let s3 = String::from("toe");
let s3 = s1 + "-" + &s2 + "-" + &s3;
println!("{}", s3);

输出tic-tac-toe

  • 使用format!宏拼接字符串
let s1 = String::from("tic");
let s2 = String::from("tac");
let s3 = String::from("toe");


let s = format!("{}-{}-{}", s1, s2, s3);
println!("{}", s);

同样输出tic-tac-toe 这个方法不会获得原来s1, s2, s3的所有权,这三个依旧可以使用

索引字符串

  • Rust 不允许使用索引获取String中的字符
let s1 = String::from("hello");
let h = s1[0]; // Error: the type `std::string::String` cannot be indexed by `{integer}`
  • String 是对Vec<u8>的封装

字节、标量值和字形簇

  • Rust 有三种看待字符串的形式:
    • 字节
    • 标量值
    • 字形簇 (最接近人类理解的字符概念)

切割String

  • 可以使用[] 和一个范围来创建一个字符串切片
    • 必须谨慎使用
    • 如果切割时跨越了字符边界, 会panic

HashMap

  • 键值对的形式储存数据,一个键(Key)对应一个值(Value)
  • Hash 函数: 决定了键和值如何储存到内存中
  • 适用场景: 通过K(任何类型)来寻找数据,而不是通过索引

创建一个HashMap

let mut scores= HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);
  • HashMap 数据储存在heap上
  • 所有的key必须是相同类型,所有的value也必须是相同类型

另外一种创建HashMap的方法

  • 在元素类型为Tuple的Vector上使用collect方法, 可以组建一个HashMap:
    • 要求Tuple 有两个值: 一个作为key, 一个作为value
    • collect 方法可以将数据收集到不同的数据结构中
      • 返回值需要显式声明类型
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();

HashMap 和所有权

  • 对于实现了Copy trait的类型,其值会被复制到HashMap中
  • 对于拥有所有权在的值(例如String),值会被移动,所有权会转移给HashMap
let field_name = String::from("Favorite color");
let field_value = String::from("Blue");

let mut map = HashMap::new();
map.insert(field_name, field_value);

//println!("{}:{}", field_name, field_value);// error! ownership moved to map
  • 如果将值的引用插入HashMap,值本身不会被移动到HashMap中
let field_name = String::from("Favorite color");
let field_value = String::from("Blue");

let mut map = HashMap::new();
map.insert(&field_name, &field_value);

println!("{}:{}", field_name, field_value);
  • 在HashMap有效时,其引用的key和value值也必须有效

访问HashMap中的值

  • 使用get方法,传入key,返回一个Option<&V>
let mut scores = HashMap::new();

scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);

let team_name = String::from("Blue");
let score = scores.get(&team_name); // returns Option<&V>

match score{
Some(s) => println!("{}: {}", team_name, s),
None => println!("{}: {}", team_name, "No score"),
};
  • 遍历HashMap
let mut scores = HashMap::new();

scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);


for (key, value) in &scores {
println!("{}: {}", key, value);
}

更新HashMap

  • HashMap 大小可变
  • 每个K只能对应一个V
  • 更新HashMap 中的数据:
    • K 已存在, 对应一个V:
      • 替换现有的V
      • 保留现有的V, 忽略新的V
      • 合并现有的V和新的V
    • K 不存在, 插入新的K和V

替换现有的V

  • 如果向HashMap插入一对KV, 然后再插入同样的K, 但是不同的V, 那么原来的V会被替换
let mut scores = HashMap::new();

scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Blue"), 50);

println!("{:?}", scores);

输出{"Blue": 50}

只在K没有对应任何值的情况下V时插入

  • entry方法: 检查指定的K是否有对应的V
    • 参数为K
    • 返回enum Entry: 代表值是否存在
  • Entry的or_insert()方法:
    • 返回:
      • 如果K存在, 返回对应的V的可变引用
      • 如果K不存在, 插入参数作为新的V, 返回新的V的可变引用
let mut scores = HashMap::new();

scores.insert(String::from("Blue"), 10);

// scores.entry(String::from("Yellow")).or_insert(50);
let e= scores.entry(String::from("Yellow"));
println!("{:?}", e);
e.or_insert(50);
scores.entry(String::from("Blue")).or_insert(50); //不会覆盖原有的值

println!("{:?}", scores);

输出{"Blue": 10, "Yellow": 50}

基于现有V来更新V

let text =  "hello world wonderful world";

let mut map = HashMap::new();

for word in text.split_whitespace() {
// or_insert 方法事实上会返回这个键的值的一个可变引用(&mut V)
// 这里我们将这个可变引用储存在 count 变量中
let count = map.entry(word).or_insert(0); //返回的是一个可变引用
*count += 1;
}

println!("{:?}", map);

输出{"world": 2, "hello": 1, "wonderful": 1}

Hash 函数

  • 默认情况下, HashMap使用加密强大的Hash函数, 可以抵抗拒绝服务攻击(DoS)
    • 不是可用的最快的Hash算法
    • 但具有良好的安全性
  • 可以指定不同的hasher来切换到不同的Hash算法
    • hasher 是一个实现了BuildHasher trait的类型
    • 例如: FnvBuilder