Rust(八)-常用的集合

151 阅读6分钟

vector

  • 由标准库提供
  • 可存储多个值
  • 只能存储相同类型的数据
  • 值在内存中连续存放
  • 创建vector
    • Vec::new 函数
    fn main() {
        let v: Vec<i32> = Vec::new();
    }
    
        fn main() {
        let v = vec![1, 2, 3, 4, 5];
    
        let third: &i32 = &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."),
        }
    }
    
    
  • 更新vector
    • 向vector添加元素,使用push方法
    fn main() {
        let mut v = vec![1, 2, 3, 4, 5];
    
        let first = &v[0];
    
        v.push(6);
    
        println!("The first element is: {}", first);
    }
    
  • 删除vector
    • 与任何其它struct一样,当vector离开作用域后
      • 它就被清理掉了
      • 它所有的元素也被清理掉了
  • 获取vector的元素 +
    fn main() {
        let v = vec![1, 2, 3, 4, 5];
    
        let third: &i32 = &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."),
        }
    }
    
  • 所有权和借用规则
    • 不能在同一作用域内同时拥有可变和不可变引用
    fn main() {
        let mut v = vec![1, 2, 3, 4, 5];
    
        let first = &v[0];
    
        v.push(6);
    
        println!("The first element is: {}", first);
    }
    $ cargo run
    Compiling collections v0.1.0 (file:///projects/collections)
    error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
     --> src/main.rs:6:5
      |
    4 |     let first = &v[0];
      |                  - immutable borrow occurs here
    5 | 
    6 |     v.push(6);
      |     ^^^^^^^^^ mutable borrow occurs here
    7 | 
    8 |     println!("The first element is: {}", first);
      |                                          ----- immutable borrow later used here
    
    For more information about this error, try `rustc --explain E0502`.
    error: could not compile `collections` due to previous error
    
    
    
  • 遍历
fn main() {
       let v = vec![100, 32, 57];
       for i in &v {
           println!("{}", i);
       }
   }
  • 使用enum来存储多种数据类型
    • enum的变体可以附加不同类型的数据
    • enum的变体定义在同一个enum的类型下
fn main() {
    enum SpreadsheetCell {
        Int(i32),
        Float(f64),
        Text(String),
    }

    let row = vec![
        SpreadsheetCell::Int(3),
        SpreadsheetCell::Text(String::from("blue")),
        SpreadsheetCell::Float(10.12),
    ];
}

string

  • rust 开发者经常会被字符串困扰的原因
    • rust倾向于暴露可能的错误
    • 字符串数据结构复杂
    • UTF-8
  • 字符串是什么
    • byte的集合
    • 一些方法
      • 能将byte 解析为文本
    • rust的核心语言层面,只有一个字符串类型:字符串切片str(&str)
    • 字符串切片:对存储在其它地方、utf-8编码的字符串的引用
      • 字符串字面值:存储在二进制文件,也是字符串切片
    • string类型
      • 来自标准库而不是核心语言
      • 可增长、可修改、可拥有
      • utf-8编码
  • 通常说的字符串是指
    • string和str
      • 标准库里用的多
      • utf-8编码
    • 本节讲的主要是string
  • 其它类型的字符串
    • Rust的标准库还包含了很多其它的字符串类型,例如: osstring, osstr, cstring, cstr
      • string vs str 后缀:拥有或借用的变体
      • 可存储不同编码的文本或在内存中以不同的形式展现
    • libray crate 针对存储字符串可提供更多的选项
  • 创建一个新的字符串(String)
    • 很多Vec< T >的操作都可用于String
    • String::new()函数
    • 使用初始值来创建string: +
      fn main() {
          let data = "initial contents";
      
          let s = data.to_string();
      
          // the method also works on a literal directly:
          let s = "initial contents".to_string();
      }
      fn main() {
          let s = String::from("initial contents");
      }
      
    • 更新string
      • push_str()方法:把一个字符串切片附加到string
      fn main() {
          let mut s1 = String::from("foo");
          let s2 = "bar";
          s1.push_str(s2);
          println!("s2 is {}", s2);
      }
      
      • push 方法: 单个字符附加到string
      fn main() {
          let mut s = String::from("lo");
          s.push('l');
      }
      
      • +: 连接字符串
        • 使用了类似这个签名的方法fn add(self, s:&str) -> String{...}
          • 标准库中的add方法使用了泛型
          • 只能把&str添加到string
          • 解引用强制转换(deref coercion)
      • format!: 连接多个字符串(例子)
        • 和println!()类似,便返回字符串
        • 不会获得参数的所有权
      fn main() {
          let s1 = String::from("tic");
          let s2 = String::from("tac");
          let s3 = String::from("toe");
      
          let s = format!("{}-{}-{}", s1, s2, s3);
      }
      fn main() {
          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
      }
      
      • 对string按索引的形式进行访问
        • 按索引语法访问string的某部分,会报错
        • Rust的字符串不支持索引语法访问
      • 内部表示
        • String是对Vec< u8 > 的包装
          • len()方法
          let hello = "Здравствуйте";
          let answer = &hello[0];
          
    • 字节、标量值、字形簇 Bytes, Scalar Values, Grapheme Clusters
      • Rust 有三种看待字符串的方式:
        • 字节
        • 标量值
        • 字形簇 (最接近所谓的“字母”)
      
          #![allow(unused)]
          fn main() {
          for c in "नमस्ते".chars() {
              println!("{}", c);
          }
          }
      
      
      • Rust 不允许对String进行索引的最后一个原因:
        • 索引操作应消耗一个常量时间(O(1))
        • 而string无法保证:需要遍历所有内容,来确定有多少个合法的字符
      • 切割string
        • 可以使用【】和一个范围来创建字符会串的切片(例子)
          • 必须谨慎使用
          • 如果切割时跨越了字符边界,程序就会panic
          
          #![allow(unused)]
          fn main() {
          let hello = "Здравствуйте";
          
          let s = &hello[0..4];
          }
          
          
      • 遍历String的方法
        • 对于标量值: chars()方法
        • 对于字节: bytes()方法
        • 对于字形簇: 很复杂,标准库未提供
      • string不简单
        • rust选择将正确处理string数据作为所有rust程序的默认行为

          • 程序员必须在处理utf-8数据之前投入更多的精力
        • 可防止在开发后期处理涉及及非ascll字符的错误

hashmap

  • HashMap<k,v>
    • 键值对的形式存储数据,一个键(key)对应一个值(Value)
    • Hash函数:决定如何在内存中存放K和V
    • 适用场景:通过K(任何类型)来寻找数据,而不是通过索引
  • 创建HashMap +
        fn main() {
          use std::collections::HashMap;
    
          let mut scores = HashMap::new();
    
          scores.insert(String::from("Blue"), 10);
          scores.insert(String::from("Yellow"), 50);
      }
    
    
    • 创建hashmap:new()函数
    • 添加数据:insert()方法
  • HashMap
    • HashMap用的较少,不在prelude中
    • 标准库对其支持罗长,没有内置的宏来创建HashMap
    • 数据存储在heap上
    • 同构的。一个hashMap中:
      • 所有的K必须是同一种类型
      • 所有的v必须是同一种类型
  • 另一种创建HashMap的方式:collect方法
    • 在元素类型为tuple的vector上使用collect方法,可以组建一个HashMap:
      • 要求tuple有两个值:一个作为K,一个作为V
      • Collect 方法可以把数据融合成很多种集合类型,包括HashMap
        • 返回值需要显式指明类型
        fn main() {
             use std::collections::HashMap;
        
             let teams = vec![String::from("Blue"), String::from("Yellow")];
             let initial_scores = vec![10, 50];
        
             let mut scores: HashMap<_, _> =
                 teams.into_iter().zip(initial_scores.into_iter()).collect();
         }
        
  • HashMap和所有权
    • 对于实现了copy trait的类型(例如:i32),值会被复制到hashMap中
    • 对于拥有所有权的值(例如String),值会被移动,所有权会转移给HashMap
    • 如果将值的引用插入到HashMap,值本身不会移动
  • 访问Hashmap的值
    • get方法
      • 参数:K
      • 返回:Option<&v>
  • 遍历hashmap +
    fn main() {
          use std::collections::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
          • 如果向hashMap插入一对KV,然后再插入同样的k,但是不同的v,那么原来的v就会被替换掉
        • 保留现有的v,忽略新的v
        • 合并现有的v和新的v
      • K 不存在
        • 添加一对K,V
          • 只有K不对应该任何值的情况下,才插入V
            • entry方法:检查指定的K是否对应该一个V
              • 参数为K
              • 返回enum entry:代表值是否存在
            • entry的or_insert()方法
              • 返回
                • 如果K存在,返回到对应的V的一个可变引用
  • Hash函数
    • 默认情况下,HashMap使用加密功能强大的Hash函数,可以抵抗拒绝服务(Dos)攻击
      • 不是可用的最快的hash算法
      • 但具有更好的安全性
    • 可以指定不同的hasher来切换到另一个函数
      • hasher是实现buildhasher trait的类型