The Rust Programming Language - 第8章 常见集合 - 8.1 Vector

8 常见集合

Rust标准库中包含一系列被称为“集合”的非常有用的数据结构。大部分其他数据类型都代表了一个值,但集合可以包含多个值

与内建的元组和数组不同,集合指向的数据存放在堆上,这意味着数据的数量不必在编译时就已知,并且还可以随着程序的运行增长和缩小。每种集合都有着不同的功能和成本,我们在实际操作中应该根据场景选择最合适的集合,本章我们将会了解三个被广泛使用的集合

vector允许我们一个挨一个存储一系列数量可变的值

字符串是字符的集合,我们之前见过String类型,本章我们会更进一步了解

哈希map允许我们将值与一个特定的键(key)相关联,这个叫做map的更通用的数据结构的特定实现

标准库中的其他类型的集合,我们可以参考文档

8.1 Vector

第一个集合类型 Vec,也被称为 vector,它允许我们在一个单独的数据结构中存储多余一个的值,所有值在内存中彼此相邻,但是vector智能存储相同类型的值,例如购物车中的产品价格

新建vector

新建一个空的vector来存储i32类型的值,可以调用Vec::new函数

let v :Vec<i32> = Vec::new();

注意:这里我们增加了类型注解,告知我们要用它来存放什么类型的值,但其实vector是用泛型实现的,可以存任何类型,在实际代码中,一旦插入值,Rust就可以推断出值的类型,所以我们很少使用类型注解,直接通过初始化来创建vec。为了方便rust还提供了一个vec!宏,它会根据我们提供的值来创建一个新的Vec,如下:

let v = vec![1,2,3]

更新vector

使用push方法向vector中增加元素

let mut v = Vec::new();

    v.push(5);
    v.push(6);
    v.push(7);
    v.push(8);

    println!("{}",v[3]); //8

丢弃vector时也会丢弃其所有变量

类似于其他struct,vector在其离开作用域时会被释放

    {
    let v = vec![1,2,3,4]
    
    //处理v

    } //<- 这里v离开作用域并被丢弃

但是当使用vector元素的引用,情况会变的复杂,我们看下面的处理

读取vector的元素:索引语法或者get方法

let v = vec![1,2,3,4,5];

    let third = &v[2];
    println!("the third element is {}",third); //the third element is 3

    match v.get(2) {
        Some(third)=>println!("the third element is {}",third), //the third element is 3
        None => println!("there is no third element."),
    }

注意:索引值是从0开始的,&和[]会返回一个元素的引用;get方法会以索引为参数返回一个Option<&T>

为什么Rust提供了两种引用元素的方法?因为程序可以选择索引值在vector中没有对应值的情况,我们看看下面的例子

let v = vec![1,2,3,4,5];

let does_not_exist1 = &v[100];
let does_not_exist2 = v.get(100);

对于索引语法,当元素不存在时,程序会发生panic,程序会崩溃

当使用get方法,它不会panic而是会返回None,如果我们想让程序继续运行时可以使用这种方式(索引不存在时可以使用提示信息提示)

match v.get(100) {
        Some(third)=>println!("the third element is {}",third), //the third element is 3
        None => println!("there is no third element."),
    } // there is no third element.
et v = vec![1,2,3,4,5];

    let third = &v[2];
    v.push(6); //cannot borrow as mutable
    println!("the third element is {}",third); //the third element is 3

在相同作用域中不能同时存在可变与不可变引用,这是为什么呢?是由于vector的工作方式,在结尾增加新元素时,空间不足无法存放时,老元素会被拷贝到另一块内存空间中与新元素一起存放,而原来的空间会被释放借用规则会阻止程序陷入悬空的情况

遍历vector中的元素

使用for循环而不是使用索引一个个访问

let v = vec![1,2,3,4,5];

    for i in &v{
        println!("{}",i);
    }
//
1
2
3
4
5

先遍历再改变

let mut v = vec![100,32,57];
    for i in &mut v {
        *i += 50;
        println!("{}",i);
    }
//
150
82
107

使用枚举来存储多种类型

vector我们知道,只能存放同种类型的值,但我们有存放不同类型值的需求我们可以使用枚举

enum SpreadSheetCell{
        Int(i32),
        Float(f64),
        Text(String),
    }
    let row = vec![
        SpreadSheetCell::Int(3),
        SpreadSheetCell::Float(10.12),
        SpreadSheetCell::Text(String::from("blue")),
    ];

Rust在编译时就必须知道vector中类型的原因在于它需要知道存储每个元素到底需要多少内存,第二个好处是可以准确的知道这个vector中允许什么类型。如果vector允许任意类型,那么当对vector元素执行操作时一个或者多个类型的值就可能会遇到错误

但是使用枚举外加match意味着Rust能在编译时就保证总会处理所有的情况

如果在编译程序时,不能确切无疑的知道运行时会存储进vector的所有类型,枚举技术就行不通了,相反可以使用trait对象,十七章我们会讲到它

现在我们了解了一些使用vector的常见方式,标准库中还有vec定义的很多其他使用方法。例如,除了push之外还有一个pop方法,它会移除并返回vector的最后一个元素

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值