Vec新建,更新,读取
Vec<T>
,也被称为 vector
。vector
允许我们在一个单独的数据结构中储存多于一个的值,它在内存中彼此相邻地排列所有的值。vector
只能储存相同类型的值。
创建一个新的空 vector
,可以调用 Vec::new
函数:
fn main() {
let v: Vec<i32> = Vec::new();
}
注意这里我们增加了一个类型注解。因为没有向这个vector
中插入任何值,Rust
并不知道我们想要储存什么类型的元素。这是一个非常重要的点。vector
是用泛型实现的。现在,所有你需要知道的就是 Vec<T>
是一个由标准库提供的类型,它可以存放任何类型,而当 Vec
存放某个特定类型时,那个类型位于尖括号中。我们告诉 Rust v
这个 Vec<T>
将存放 i32
类型的元素。
通常,我们会用初始值来创建一个 Vec<T>
而 Rust
会推断出储存值的类型,所以很少会需要这些类型注解。为了方便 Rust
提供了 vec!
宏,这个宏会根据我们提供的值来创建一个新的 vector
:
fn main() {
let v = vec![1, 2, 3, 4];
}
Rust
推断为 i32
是因为这是默认整型类型,因为我们提供了 i32
类型的初始值,Rust
可以推断出 v
的类型是 Vec<i32>
,因此类型注解就不是必须的。
新建一个vector
并向其增加元素,可以使用 push
方法:
fn main() {
let mut v: Vec<i32> = Vec::new();
v.push(1);
v.push(2);
v.push(3);
}
注意如果想要能够改变它的值,必须使用mut
关键字使其可变。放入其中的所有值都是 i32
类型的,而且 Rust
也根据数据做出如此判断,所以不需要 Vec<i32>
注解。
有两种方法引用 vector
中储存的值:通过索引
或使用 get
方法:
fn main() {
let v = vec![1, 2, 3, 4, 5, 6];
let t = &v[2];
println!("索引取第3个值:{}", t);
if let Some(t) = v.get(2) {
println!("get取第3个值:{}", t);
} else {
println!("超出索引");
}
}
这里有几个细节需要注意。我们使用索引值 2
来获取第三个元素,因为索引是从数字 0
开始的。使用 &
和 []
会得到一个索引位置元素的引用。当使用索引作为参数调用get
方法时,会得到一个可以用于 match
的 Option<&T>
,使用get
读取值的时候返回Option<&T>
的好处在于可以很好判断是否超出索引范围。
一旦程序获取了一个有效的引用,借用检查器将会执行所有权和借用规则来确保 vector
内容的这个引用和任何其他引用保持有效。回忆一下不能在相同作用域中同时存在可变和不可变引用的规则。当我们获取了 vector
的第一个元素的不可变引用并尝试在 vector
末尾增加一个元素的时候,如果尝试在函数的后面引用这个元素是行不通的:
fn main() {
let mut v = vec![1, 2, 3, 4, 5, 6];
let f = &[2];
v.push(7);
println!("{}", f);
}
编译会给出这个错误:
$ cargo run
Compiling variables v0.1.0 (/projects/variables)
error[E0277]: `[{integer}; 1]` doesn't implement `std::fmt::Display`
--> src/main.rs:8:20
|
8 | println!("{}", f);
| ^ `[{integer}; 1]` cannot be formatted with the default formatter
|
= help: the trait `std::fmt::Display` is not implemented for `[{integer}; 1]`
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
= note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
For more information about this error, try `rustc --explain
为什么第一个元素的引用会关心 vector
结尾的变化?不能这么做的原因是由于 vector
的工作方式:在vector
的结尾增加新元素时,在没有足够空间将所有元素依次相邻存放的情况下,可能会要求分配新内存并将老的元素拷贝到新的空间中。这时,第一个元素的引用就指向了被释放的内存。借用规则阻止程序陷入这种状况。
如果想要依次访问 vector
中的每一个元素,我们可以遍历其所有的元素而无需通过索引一次一个的访问。如何使用 for
循环来获取 i32
值的 vector
中的每一个元素的不可变引用并将其打印:
fn main() {
let v = vec![1, 2, 3, 4, 5, 6];
for val in &v {
println!("{}", val);
}
}
我们也可以遍历可变 vector
的每一个元素的可变引用以便能改变他们:
fn main() {
let mut v = vec![1, 2, 3, 4, 5, 6];
for val in &mut v {
*val += 1;
println!("{}", val);
}
}
为了修改可变引用所指向的值,在使用 +=
运算符之前必须使用解引用运算符(*)
获取 i
中的值。
vector
只能储存相同类型的值。这是很不方便的;绝对会有需要储存一系列不同类型的值的用例。枚举的成员都被定义为不相同的枚举类型,所以当需要在 vector
中储存不同类型值时,我们可以定义并使用一个枚举:
#[derive(Debug)]
enum SpreadsheetCell {
Int(i32),
Float(f32),
Text(String),
}
fn main() {
let v = vec![
SpreadsheetCell::Int(1),
SpreadsheetCell::Float(1.1),
SpreadsheetCell::Text(String::from("hello world")),
];
println!("{:#?}", v);
}
Rust
在编译时就必须准确的知道 vector
中类型的原因在于它需要知道储存每个元素到底需要多少内存。第二个好处是可以准确的知道这个 vector
中允许什么类型。如果 Rust
允许 vector
存放任意类型,那么当对vector
元素执行操作时一个或多个类型的值就有可能会造成错误。使用枚举外加match
意味着 Rust
能在编译时就保证总是会处理所有可能的情况。
类似于任何其他的 struct
,vector
在其离开作用域时会被释放:
fn main() {
{
let v = vec![1, 2, 3, 4];
// v 可以继续使用
} // <- v 超出范围并在此处释放
}
当vector
被丢弃时,所有其内容也会被丢弃,这意味着这里它包含的整数将被清理。
标准库 Vec
的 API 文档。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!