当前位置: 首页>>代码示例 >>用法及示例精选 >>正文


Rust Vec用法及代码示例


本文简要介绍rust语言中 Struct alloc::vec::Vec 的用法。

用法

pub struct Vec<T, A: Allocator = Global> { /* fields omitted */ }

一种连续可增长的数组类型,写作Vec<T>,发音为'vector'。

例子

let mut vec = Vec::new();
vec.push(1);
vec.push(2);

assert_eq!(vec.len(), 2);
assert_eq!(vec[0], 1);

assert_eq!(vec.pop(), Some(2));
assert_eq!(vec.len(), 1);

vec[0] = 7;
assert_eq!(vec[0], 7);

vec.extend([1, 2, 3].iter().copied());

for x in &vec {
    println!("{}", x);
}
assert_eq!(vec, [7, 1, 2, 3]);

提供 vec! 宏是为了方便初始化:

let mut vec1 = vec![1, 2, 3];
vec1.push(4);
let vec2 = Vec::from([1, 2, 3, 4]);
assert_eq!(vec1, vec2);

它还可以使用给定值初始化Vec<T> 的每个元素。这可能比在单独的步骤中执行分配和初始化更有效,尤其是在初始化零向量时:

let vec = vec![0; 5];
assert_eq!(vec, [0, 0, 0, 0, 0]);

// The following is equivalent, but potentially slower:
let mut vec = Vec::with_capacity(5);
vec.resize(5, 0);
assert_eq!(vec, [0, 0, 0, 0, 0]);

有关详细信息,请参阅容量和重新分配。

使用 Vec<T> 作为高效堆栈:

let mut stack = Vec::new();

stack.push(1);
stack.push(2);
stack.push(3);

while let Some(top) = stack.pop() {
    // Prints 3, 2, 1
    println!("{}", top);
}

索引

Vec 类型允许按索引访问值,因为它实现了 Index 特征。一个例子会更明确:

let v = vec![0, 2, 4, 6];
println!("{}", v[1]); // it will display '2'

但要小心:如果您尝试访问不在 Vec 中的索引,您的软件将会出现Panics!你不可以做这个:

let v = vec![0, 2, 4, 6];
println!("{}", v[6]); // it will panic!

如果要检查索引是否在 Vec 中,请使用 get get_mut

切片

Vec 可以是可变的。另一方面,切片是只读对象。要获得 slice ,请使用 & 。例子:

fn read_slice(slice: &[usize]) {
    // ...
}

let v = vec![0, 1];
read_slice(&v);

// ... and that's all!
// you can also do it like this:
let u: &[usize] = &v;
// or like this:
let u: &[_] = &v;

在 Rust 中,当您只想提供读取访问权限时,将切片作为参数而不是向量传递更为常见。 String &str 也是如此。

容量和重新分配

向量的容量是为将添加到向量上的任何未来元素分配的空间量。不要与向量的长度混淆,向量的长度指定向量中实际元素的数量。如果一个向量的长度超过了它的容量,它的容量会自动增加,但它的元素必须重新分配。

例如,容量为 10 且长度为 0 的向量将是一个空向量,其空间可容纳 10 个以上的元素。将 10 个或更少的元素推送到向量上不会改变其容量或导致发生重新分配。但是,如果向量的长度增加到 11,则必须重新分配,这可能会很慢。出于这个原因,建议尽可能使用 Vec::with_capacity 来指定向量的预期大小。

保证

由于其令人难以置信的基本性质,Vec 对其设计做出了很多保证。这确保它在一般情况下尽可能为low-overhead,并且可以通过不安全的代码以原始方式正确操作。请注意,这些保证是指不合格的 Vec<T> 。如果添加了额外的类型参数(例如,支持自定义分配器),覆盖它们的默认值可能会改变行为。

最根本的是,Vec 是并且永远都是(指针、容量、长度)三元组。不多也不少。这些字段的顺序是完全未指定的,您应该使用适当的方法来修改它们。指针永远不会为空,所以这个类型是null-pointer-optimized。

但是,指针可能实际上并不指向分配的内存。特别是,如果您构建一个Vec容量为 0 通过Vec::new,alloc::vec,alloc::vec::Vec.with_capacity,或通过调用alloc::vec::Vec.shrink_to_fit在一个空的 Vec 上,它不会分配内存。同样,如果您将零大小的类型存储在Vec,它不会为它们分配空间。请注意,在这种情况下,Vec 可能不会报告 capacity 为 0.Vec当且仅当mem::size_of::<T>() * capacity() > 0.一般来说,Vec的分配细节非常微妙——如果你打算使用Vec并将其用于其他用途(传递给不安全的代码,或构建您自己的memory-backed 集合),请务必使用from_raw_parts恢复Vec然后放下它。

如果一个Vec 分配的内存,那么它指向的内存在堆上(由分配器定义,Rust 配置为默认使用),它的指针指向len按顺序初始化的连续元素(如果将其强制为切片,您会看到什么),然后是capacity - len逻辑上未初始化的连续元素。

包含元素 'a''b' 且容量为 4 的向量可以如下所示进行可视化。顶部是Vec 结构,它包含指向堆中分配头的指针、长度和容量。底部是堆上的分配,一个连续的内存块。

ptr      len  capacity
       +--------+--------+--------+
       | 0x0123 |      2 |      4 |
       +--------+--------+--------+
            |
            v
Heap   +--------+--------+--------+--------+
       |    'a' |    'b' | uninit | uninit |
       +--------+--------+--------+--------+
  • 初始化表示未初始化的内存,请参见MaybeUninit.
  • 注意:ABI 不稳定,Vec 不保证其内存布局(包括字段的顺序)。

Vec 永远不会执行 “small optimization” 其中元素实际存储在堆栈中的原因有两个:

  • 这将使不安全代码更难以正确操作 Vec 。如果仅移动Vec 的内容,则它的内容将没有稳定的地址,并且很难确定Vec 是否实际分配了内存。

  • 它会惩罚一般情况,每次访问都会产生一个额外的分支。

Vec 永远不会自动收缩自身,即使完全为空。这确保不会发生不必要的分配或释放。清空 Vec 然后将其填充回相同的 len 应该不会调用分配器。如果您想释放未使用的内存,请使用 shrink_to_fit shrink_to

alloc::vec::Vec.pushalloc::vec::Vec.insert如果报告的容量足够,则永远不会(重新)分配。alloc::vec::Vec.pushalloc::vec::Vec.insert 将要(重新)分配如果len == capacity.也就是说,报告的容量是完全准确的,并且可以依赖。它甚至可以用于手动释放由Vec如果需要的话。批量插入方法可能重新分配,即使没有必要。

Vec不保证任何特定的增长策略在重新分配时已满,也不保证任何特定的增长策略alloc::vec::Vec.reserve叫做。当前的策略是基本的,并且可能证明使用非恒定增长因子是可取的。无论使用什么策略,当然都会保证O(1)摊销alloc::vec::Vec.push.

vec![x; n]vec![a, b, c, d] Vec::with_capacity(n) 都将生成具有完全请求容量的 Vec。如果 len == capacity ,(如 vec! 宏的情况),则可以将 Vec<T> 转换为 Box<[T]> 和从 Box<[T]> 转换,而无需重新分配或移动元素。

Vec 不会专门覆盖从中删除的任何数据,但也不会专门保留它。其未初始化的内存是临时空间,它可以随意使用。它通常只会做最有效或最容易实现的事情。请勿出于安全目的而依赖删除已删除的数据。即使您删除 Vec ,其缓冲区也可能会被另一个分配重用。即使您首先将 Vec 的内存清零,实际上也可能不会发生,因为优化器不认为这是必须保留的副作用。不过,有一种情况我们不会打破:使用unsafe代码写入多余的容量,然后增加长度以匹配,总是有效的。

目前,Vec 不保证删除元素的顺序。顺序过去已更改,可能会再次更改。

相关用法


注:本文由纯净天空筛选整理自rust-lang.org大神的英文原创作品 Struct alloc::vec::Vec。非经特殊声明,原始代码版权归原作者所有,本译文未经允许或授权,请勿转载或复制。