Skip to content

Rust容器常用操作

读取文件内容到字符串

rust
let content = fs::read_to_string(input_file)?;

以 UTF-8 格式读取

rust
let bytes = fs::read(input_file)?;
let content = String::from_utf8_lossy(&bytes);

它 Rust 里宏大多是小写开头,然后类是大写开头,所以

rust
let a = vec![1, 3]; //vec 是小写
println!("{}", count);

Vec

构建

rust
let mut vec0 = Vec::new();
let mut vec1 = vec![1, 2, 3]; //建议是这个宏
let vec2 = Vec::from([1, 2, 3, 4]);

求最大

rust
nums.iter().max()
nums.into_iter().max()

求和

rust
nums.iter().sum()
nums.into_iter().sum()

排序

rust
nums.sort()

访问元素

rust
let a = nums[8];
let a = nums.get(8); // a:Option<&i32>

前者没有越界检查,越界就死。后者有检查,返回 Option。

备注:各个标准容器都有 get 方法

遍历

正常遍历

rust
for x in &vec {}

for (index, x) in nums.iter().enumerate()
for index in 0..vec.len() {}

反向遍历

rust
for &item in vec.iter().rev() // 推荐
for item in vec.into_iter().rev()

for (i, &value) in vec.iter().enumerate().rev()

差异:

rust
let v = vec![1, 2, 3];
    for &x in v.iter(){}

    for x in v.iter(){}
    
    for x in v{}
    
    for &x in &v{}

    for x in v.into_iter(){}
写法元素类型所有权v 是否可用等价写法
for &x in v.iter()i32借用-
for x in v.iter()&i32借用-
for x in vi32移动for x in v.into_iter()
for &x in &vi32借用for x in v.iter().copied()
for x in &v&i32借用for x in v.iter()

关键点:for 循环中的 x 是一个模式(pattern),而不仅仅是变量名

最常写的是第三种 for x in v ,但是这会消耗掉 v

第一种:

  • &x 是一个模式,它匹配一个引用并将其解构

  • 当迭代器产生 &i32 时,模式 &x 可以匹配它

  • 匹配后,x 绑定到 i32

  • 对于实现了 Copy trait 的类型(如 i32),会 Copy 到 x 上,如果没有实现,不让这样写

第二种:

  • x 是一个简单的标识符模式
  • 它会匹配任何值

如果想用 for &x 这种写法,结构体必须实现 Copy trait。

链式调用

在 Rust 中实现链式调用的关键在于每个方法都返回self(或同类型)以支持连续调用。

rust
    let a = vec![1,2,3];
    let mut b = Vec::new();
    for x in &a {
        if x > 2 {
            b.push(x * 2);
        }
    }
	// Rust 链式风格
    let b: Vec<_> = a.iter()
        .filter(|&&x| x > 2)
        .map(|&x| x * 2)
        .collect();

1、第一步,**into_iter **取出迭代器,而不是用 “增强 for 循环” 取出元素

2、如果你需要过滤,则考虑 filter,它接受一个 lambda 表达式

3、利用** map **函数进行操作,它也接受一个 lambda 表达式

4、用 take、skip 做跳过

5、利用** collect **将迭代器转会容器

rust
vec![1, 2, 3, 4, 5]
    .into_iter()          // 转为迭代器
    .filter(|x| x % 2 == 0)  // 过滤偶数
    .map(|x| x * 2)       // 映射
    .take(3)              // 取前 3 个
    .skip(1)              // 跳过第 1 个
    .chain(vec![10, 11])  // 连接其他迭代器
    .enumerate()          // 添加索引
    .collect::<Vec<_>>(); // 收集结果

Map

所有权:

HashMap 持有值的所有权,所以值会被转移进去

因此,在函数设计上,它的访问函数,比如 get,它是需求引用,而 insert 是需求值

构建

构建一个容器,你需要的是把泛型写在前面,而后面只需要写类名

rust
let mut a: HashMap<i32, Vec<i32>> = HashMap::new();
let mut b: Vec<i32> = Vec::new();

遍历

类似于 C++ 用 it 来遍历键值对(取 it,然后访问 it->second)是:

rust
for (k, v) in my_map.iter_mut(){
    v.sort();
}

rust 更灵活一些,可以仅遍历值或者键

rust
for k in my_map.keys()
for v in my_map.values_mut()
for v in my_map.values()

修改

比如当前键值是 a->1 ,修改为 a->2

这样写不对

rust
if let Some(index) = char_map.get(&c){
    char_map.insert(c, index2);
    max_len = right - *index; // 我的版本里,这里写不行,因为这样属于是在 insert 之后用 index,如果你放前面,我现在是可以的,老版本据说不行
}

建议是 Entry 函数

rust
match char_map.entry(c) {
    Entry::Occupied(entry) => {
        let old_index = *entry.get();
        entry.insert(right);
        max_len = right - old_index;
    }
    Entry::Vacant(entry) => {
        entry.insert(right);
        // 其他逻辑
    }
}
rust
let a :HashMap<i32, Vec<i32>>
if let Some(vec_x) = a.get_mut(&y){
            vec_x.push(x);
        }else{
            y_map_unorder.insert(y,vec![x]);
        }

// 改为

a.entry(x).or_default().push(y);

Option

模式匹配取值

1、if let

一般情况下只关心 Some

备注:在这里可以考虑变量遮蔽

rust
let map : HashMap<i32, char>
if let Some(v) = map.get(k){
    //
}else{
    //
}

2、如果需要关心 Some

备注:依然可以考虑变量遮蔽

rust
let v = map.get(k)
match v{
    Some(v) => {
    }
    None => //
}

取值

unwrap 系列方法

rust
let x = Some(10).unwrap(); // 如果 None,则 Panic
let x = Some(10).unwrap_or(0)

unwrap() 返回的是一个引用

String

遍历

rust
for (i, c) in s.chars().enumerate()

读写文件

rust
use std::path::Path;
use std::fs::File;
use std::io::{BufRead, BufReader};

1、指定文件路径

rust
let path : &Path = Path::new("./JRoPlatform.h");

备注:类型是 &Path 是因为 Path 类似于 str 是动态大小的,所以无法编译前确定,因此只能返回引用

而 Vec 那种,实际上 Vec 就是个智能指针,就直接返回指针本体了

2、打开文件

rust
let file : File = File::open(file_path)
        .with_context(|| format!("无法打开文件:{}", file_path.display()))?;

3、构造 Buffer 读取

rust
let reader : BufReader<File> = BufReader::new(file);

4、按行遍历

rust
for line in reader.lines() {
        let line : String = line?;
    }

直接读取到字符串里:

rust
let s = fs::read_to_string("xxx.h")?;

&str

理解

按照 Rust 文档的描述:

str 是一个类型,它的地位和 bool、i32 一样

它常常以借用的形式 &str 出现

&str 就像 C 语言的 const char*,但 Rust 的借用检查器确保它永远不会悬空

你在使用 &str 的时候,不需要管,&str 到底指向了哪里。是静态区,还是临时 String,还是某个结构体,总之,它是有效的。

1、&str 的本质

指针+长度

长度是字节数,而不是字符数

2、str 的编码要求必须是 utf-8

3、&str 是不可变引用,所以不可以修改。也不拥有数据

4、字符的个数 .chars().count()

5、len() 字节为单位。换句话说,它可能与人类所理解的字符串长度有所不同。

使用场景

  1. 数据需要被修改吗? → 用 String
  2. 需要存储数据,而不仅仅是查看吗? → 用 String
  3. 只是读取已有数据,不获取所有权? → 用 &str
  4. 编写库函数,希望用户友好? → 参数用 &str (&str 可以接受 &String,而反之不行)
  5. 处理字符串字面量或临时切片? → 用 &str
场景适合使用 &str原因
配置/元数据通常是编译时已知的常量
临时视图短时间借用,不需要所有权
解析器状态指向正在解析的文本
数据库字段需要长期存储,用 String
用户输入需要拥有数据,用 String