30天拿下Rust之向量

简介: 30天拿下Rust之向量

概述

在Rust语言中,向量(Vector)是一种动态数组类型,可以存储相同类型的元素,并且可以在运行时改变大小。向量是Rust标准库中的一部分,位于std::vec模块中。向量是一个非常灵活和强大的数据结构,可以方便地用于各种场景,包括:存储数据、处理集合、构建动态数组等。

向量的创建

向量类型由标准库中的Vec<T>结构体实现,这里的T是类型参数,代表向量能够存储任何类型的单个值,但所有元素必须是同一类型。Rust的向量是在堆上分配的,这意味着当我们创建一个向量时,它会在堆上分配内存,而不是在栈上。因此,当向量超出作用域时,Rust会自动释放其占用的内存,防止内存泄漏。

在Rust中,创建向量主要有以下4种方法。

1、使用Vec::new创建空向量。

let mut vec_number: Vec<i32> = Vec::new();


2、使用vec!宏创建带有初始值的向量。

fn main() {
    // 创建一个整数向量
    let vec_number: Vec<i32> = vec![66, 99, 100];
    println!("{:?}", vec_number);
    // 创建一个字符串向量
    let vec_str: Vec<String> = vec!["Hope".to_string(), "GitHub".to_string()];
    println!("{:?}", vec_str);
}


3、使用vec!宏创建指定长度,并初始化所有元素为相同值的向量。

fn main() {
    let vec_number: Vec<i32> = vec![0; 5];
    // 输出:[0, 0, 0, 0, 0]
    println!("{:?}", vec_number);
}


4、使用Vec::with_capacity创建具有特定容量,但长度为0的向量。

fn main() {
    // 创建一个初始为空,但足够存储5个整型元素的向量
    let mut vec_number: Vec<i32> = Vec::with_capacity(5);
    // 输出:[]
    println!("{:?}", vec_number);
}


向量的访问

访问向量的元素,可以通过索引或迭代器进行。

1、通过索引访问。注意:Rust中的索引是基于0的,即第一个元素的索引是0,最后一个元素的索引是向量长度减1。在Rust中使用索引访问元素,没有运行时边界检查。如果索引超过边界,会导致程序崩溃。为了更安全地使用索引访问,避免因索引越界引发崩溃,可以使用get()方法或get_mut()方法,它返回一个Option<&T>或Option<&mut T>。

fn main() {
    let mut vec_number = vec![66, 99, 100];
    // 访问第一个元素,输出:66
    println!("{}", vec_number[0]);
    // 修改第二个元素
    vec_number[2] = 88;
    // 安全访问,当索引不存在时,不会panic
    if let Some(value) = vec_number.get(2) {
        // 输出:88
        println!("{}", value);
    } else {
        println!("out of bounds");
    }
    // 对可变引用的安全访问,可修改向量中的元素
    let mut vec_mut = vec![1, 2, 3];
    if let Some(value) = vec_mut.get_mut(1) {
        *value = 1024;
    }
    // 输出:[1, 1024, 3]
    println!("{:?}", vec_mut);
   
    // 获取向量的长度,输出:3
    println!("{}", vec_mut.len());
    // 清空向量,并判断向量是否为空
    vec_mut.clear();
    if vec_mut.is_empty() {
        println!("empty");
    } else {
        println!("not empty");
    }
}



2、通过迭代器访问。迭代器(Iterator)是访问向量元素的一种安全、高效且灵活的方式。通过迭代器可以遍历向量中的所有元素,并对它们执行操作,而无需关心具体的索引。iter()方法用于获取只读迭代器,iter_mut()方法用于获取可写迭代器,into_iter()方法用于消耗向量并迭代。

fn main() {
    let mut vec_str = vec!["Hope", "GitHub", "Gitee"];
    // 使用for循环遍历向量的所有元素
    for s in &vec_str {
        println!("{}", s);
    }
   
    // 使用iter()显式获取迭代器
    for s in vec_str.iter() {
        println!("{}", s);
    }
   
    // 若要修改元素,需使用iter_mut()获得可变引用迭代器
    for s in vec_str.iter_mut() {
        if s.starts_with("C") {
            *s = "Rust";
        }
    }
    println!("{:?}", vec_str);
   
    for s in vec_str.into_iter() {
        println!("{}", s);
    }
    // 到这里时,vec_str不再有效,因为它已经被消耗掉了
    // println!("{:?}", vec_str);
}



向量的修改

1、添加、移除元素。使用push方法可以将元素添加到向量的末尾,使用pop方法可以从向量的末尾移除元素并返回该元素。

fn main() {
    let mut vec_number = vec![10, 20];
    vec_number.push(66);
    // 输出:[10, 20, 66]
    println!("{:?}", vec_number);
    let last_number = vec_number.pop();
    // 输出:66
    if let Some(value) = last_number {
        println!("{}", value);
    } else {
        println!("no data");
    }
   
    // 输出:[10, 20]
    println!("{:?}", vec_number);
}


2、插入、删除元素。insert)方法用于将元素插入到指定索引位置,原有位置及之后的元素都会右移。remove()方法用于删除并返回指定索引位置的元素,之后的元素会左移填补空位。注意:插入和删除操作可能导致向量内部需要重新分配内存以适应大小的变化,这可能涉及到元素的移动;因此,对于大型数据集或性能敏感的应用,这些操作可能会有较高的时间开销。

fn main() {
    let mut vec_number = vec![10, 20];
    // 插入元素
    vec_number.insert(1, 66);
    // 输出:[10, 66, 20]
    println!("{:?}", vec_number);
    // 删除元素
    vec_number.remove(0);
    // 输出:[66, 20]
    println!("{:?}", vec_number);
}



向量的切片

Rust的向量支持切片操作,这意味着,我们可以获取向量的一部分作为新的向量。切片是通过指定起始索引和结束索引来创建的,注意:包括起始索引的元素,但不包括结束索引的元素。

fn main() {
    let mut vec_number = vec![10, 20, 30, 40, 50];
    // 获取一个切片,从索引2到4(不包括4)
    let slice = &mut vec_number[2..4];
    // 输出:[30, 40]
    println!("{:?}", slice);
    // 修改切片中的元素
    for item in slice.iter_mut() {
        *item *= 2;
    }
    // 输出:[60, 80]
    println!("{:?}", slice);
}



向量的排序

Rust的向量可以通过sort()方法或sort_unstable()来进行排序,还可以通过sort_by()方法或sort_unstable_by()方法根据自定义的比较逻辑来进行排序。sort()方法是稳定的排序算法,即相同元素的相对顺序不会改变。sort_unstable()方法是不稳定的排序算法,意味着相同的元素可能会改变相对顺序,但总体上会按照给定的排序规则排序。

fn main() {
    let mut vec_number = vec![7, 88, 12, 36, 50];
    // 默认为升序排序
    vec_number.sort_unstable();  
    // 输出:[7, 12, 36, 50, 88]
    println!("{:?}", vec_number);
    // 显式指定排序规则,仍然是升序
    vec_number.sort_by(|a, b| a.cmp(b));
    // 输出: [7, 12, 36, 50, 88]
    println!("{:?}", vec_number);
    // 若要降序排序,反转比较结果
    vec_number.sort_unstable_by(|a, b| b.cmp(a));
    // 输出: [88, 50, 36, 12, 7]
    println!("{:?}", vec_number);
}



向量的扩展和收缩

在Rust中,向量提供了几种方法来管理其容量和大小。容量是向量在内存中为元素预留的空间大小,而大小则是向量当前实际包含的元素数量。当添加元素到向量时,如果当前容量不足,向量可能会自动增长其容量。但这并不意味着当我们删除元素时,向量会自动缩小其容量。

以下是向量中用于管理容量的几个方法。

1、resize方法用于改变容量的大小。如果新的大小大于当前大小,向量会在末尾添加默认值(对于整数类型,通常是0;对于其他类型,则依赖于其默认构造函数)。如果新的大小小于当前大小,向量会丢弃末尾的多余元素。

fn main() {
    let mut vec_number = vec![10, 20, 30];
    // 向量为[10, 20, 30, 0, 0],大小为5,容量可能增长以容纳更多元素
    vec_number.resize(5, 0);
    println!("{:?}", vec_number);
    // 向量为[10, 20],大小为2,但容量可能仍然大于2
    vec_number.resize(2, 0);
    println!("{:?}", vec_number);
}


2、reserve方法用于确保向量至少有足够的容量来存储指定数量的元素,而不会重新分配。如果当前容量小于所需容量,向量会分配更多内存,这不会改变向量的大小。

fn main() {
    let mut vec_number = vec![10, 20, 30];
    // 确保vec至少有20的容量,但不改变其大小和内容
    vec_number.reserve(20);
    // 输出:[10, 20, 30]
    println!("{:?}", vec_number);
}


3、shrink_to_fit方法尝试将向量的容量减少到与其大小相同。这通常在我们确定不再需要额外容量,并且希望减少内存使用时很有效。注意:这个方法并不保证一定能减少容量,某些情况下,出于性能或实现的原因,向量可能会保留一些额外的容量。

fn main() {
    let mut vec_number = vec![10, 20, 30];
    // 将容量增加到10
    vec_number.reserve(10);
    // 删除一个元素,现在大小是2,但容量可能仍然是10
    vec_number.pop();
    // 尝试将容量减少到2
    vec_number.shrink_to_fit();
    println!("{}", vec_number.capacity());
}



注意:在使用上述这些方法时,我们应该考虑性能开销和内存使用之间的权衡。频繁地调整向量的容量可能会导致不必要的性能开销,因此在确定确实需要调整容量时,才应该使用这些方法。

向量的连接和合并

在Rust中,如果想要连接或合并多个集合,可以使用extend方法,或者利用迭代器的collect方法。在下面的示例代码中,我们使用extend方法后,vec2被转移了所有权,已经无效了。collect方法通常与迭代器一起使用,用于将迭代器中的元素收集到一个集合中。

fn main() {
    let mut vec1 = vec![1, 2, 3];
    let vec2 = vec![4, 5, 6];
   
    // 将vec2的内容添加到vec1末尾
    vec1.extend(vec2);
    // 输出:[1, 2, 3, 4, 5, 6]
    println!("{:?}", vec1);
    // vec2被extend函数转移了所有权,到这里时无效了
    // println!("{:?}", vec2);
   
    // 创建一个新的向量来包含两个向量的内容
    let vec3 = vec![1, 2, 3];
    let vec4 = vec![4, 5, 6];
    let vec5: Vec<i32> = vec3.into_iter().chain(vec4).collect();
    // 输出:[1, 2, 3, 4, 5, 6]
    println!("{:?}", vec5);
}


相关文章
|
存储 传感器 Rust
Rust 笔记:Rust 语言中使用 vector(向量)
本文介绍 Rust 语言中的向量,包括向量与数组的区别,向量的相关API的用法等等。
9762 3
|
存储 Rust 索引
【RUST学习日记】第11课 向量 (下)
【RUST学习日记】第11课 向量 (下)
|
Rust 索引
【RUST学习日记】第11课 向量 (上)
【RUST学习日记】第11课 向量 (上)
|
2月前
|
Rust 安全 Go
揭秘Rust语言:为何它能让你在编程江湖中,既安全驰骋又高效超车,颠覆你的编程世界观!
【8月更文挑战第31天】Rust 是一门新兴的系统级编程语言,以其卓越的安全性、高性能和强大的并发能力著称。它通过独特的所有权和借用检查机制解决了内存安全问题,使开发者既能享受 C/C++ 的性能,又能避免常见的内存错误。Rust 支持零成本抽象,确保高级抽象不牺牲性能,同时提供模块化和并发编程支持,适用于系统应用、嵌入式设备及网络服务等多种场景。从简单的 “Hello World” 程序到复杂的系统开发,Rust 正逐渐成为现代软件开发的热门选择。
56 1
|
9天前
|
Rust 安全 网络安全
在 Rust 语言中,寻找企业上网行为管理软件的突破
在数字化企业环境中,上网行为管理软件对于保障网络安全和提升工作效率至关重要。Rust 语言凭借其安全性、高性能和并发性,为开发此类软件提供了新机遇。本文通过几个 Rust 代码示例,展示了如何实现网址检查、访问频率统计及访问控制等功能,旨在探索 Rust 在企业上网行为管理中的应用潜力。
19 0
|
2月前
|
Rust 安全 编译器
初探 Rust 语言与环境搭建
Rust 是一门始于2006年的系统编程语言,由Mozilla研究员Graydon Hoare发起,旨在确保内存安全而不牺牲性能。通过所有权、借用和生命周期机制,Rust避免了空指针和数据竞争等问题,简化了并发编程。相较于C/C++,Rust在编译时预防内存错误,提供类似C++的语法和更高的安全性。Rust适用于系统编程、WebAssembly、嵌入式系统和工具开发等领域。其生态系统包括Cargo包管理器和活跃社区。学习资源如&quot;The Book&quot;和&quot;Rust by Example&quot;帮助新手入门。安装Rust可通过Rustup进行,支持跨平台操作。
138 2
初探 Rust 语言与环境搭建
|
2月前
|
Rust 安全 程序员
Rust 语言的防错机制太惊人了!安全编码从此不再是难题,快来一探究竟!
【8月更文挑战第31天】《安全编码原则:Rust 语言中的防错机制》探讨了代码安全的重要性,并详细介绍了Rust语言如何通过内存安全模型、所有权与借用规则等特性,在编译阶段检测并阻止潜在错误,如缓冲区溢出和悬空指针。文章还讨论了类型安全、边界检查等其他安全特性,并提出了遵循不可变引用、避免裸指针及充分测试等实用编码原则,以进一步提升代码质量和安全性。随着Rust在软件开发中的应用日益广泛,掌握其安全编码原则变得尤为重要。
50 0
|
2月前
|
Rust 安全 调度
从零构建梦想操作系统:用Rust语言亲手打造专属内核,你也可以成为系统开发者!
【8月更文挑战第31天】开发操作系统内核虽具挑战,却也充满乐趣。本文将指导你从零开始,使用Rust语言构建一个简单的操作系统内核。首先安装Rust环境和交叉编译工具链,然后创建新项目`my_kernel`。通过修改`Cargo.toml`和编写启动函数,结合串口输出和`multiboot2`库,最终使用QEMU运行内核。此教程旨在帮助你理解Rust在系统开发中的应用,激发深入探索的兴趣。
68 1
|
2月前
|
Rust 安全 算法
揭秘Rust语言如何重塑区块链安全:打造坚不可摧的分布式账本新篇章!
【8月更文挑战第31天】自比特币诞生以来,区块链技术凭借其去中心化和不可篡改的特点备受关注。为了应对安全性挑战,Rust 语言凭借其内存安全特性逐渐成为区块链开发的优选。本文探讨了 Rust 如何助力区块链实现更安全的分布式账本。通过示例展示了 Rust 在避免内存泄漏、空指针引用及数据竞争等方面的优势,预示着 Rust 在高性能、高安全性需求的区块链应用中拥有广阔前景。
70 1
|
1月前
|
Rust Linux Go
Rust/Go语言学习
Rust/Go语言学习