Rust数组与向量
数组实现了IntoIterator trait,可以使用for-in迭代,for-in会自动调用数组的into_iter方法转换成迭代器。在Rust中,从数组到切片是隐式的,而从切片转换到数组不是隐式的。当我们尝试在数组中移动一个元素,这会导致数组的一部分内容不再被数组拥有,是不可预算的行为。一维数组只由列组成,二维数组是由行和列组成的,而三维数组是由行、列和深度组成的。数组类型需要手动声明,并且
数组
- 数组是一种原生类型,可以在标准预制库中找到。
- 由固定数量的相同类型的元素组成。
数组的大小必须在编译时确定。在[]
内指定数组的元素类型和数量,如
array_name[type: length]
[value1, value2, value3, ..., valueN]
[value; repeat];
例如
fn main() {
let array_1: [i32; 3] = [1, 2, 3];
let array_2 = [4, 5, 6];
let array_3 = [7; 3];
println!("{:?}",array_1);
println!("{:?}",array_2);
println!("{:?}",array_3);
}
因为数组实现了Debug trait,因此我们可以通过{:?}
占位符打印数组。
我们可以通过len方法输出数组的长度
fn main() {
let array = [1,2,3,4,5,6,7];
println!("{}", array.len());
}
多维数组
一维数组只由列组成,二维数组是由行和列组成的,而三维数组是由行、列和深度组成的。维度的数量是没有限制的。
多维数组的声明语法如下(以三维举例):
[[[type; elements]; elements]; elements]
访问数组元素
数组下标从0开始的
fn main() {
let array = [[[1,2,3,4],[5,6,7,8]],[[1,2,3,4],[5,6,7,8]],[[7,8,9,10],[9,10,11,12]]];
println!("{}",array[0][1][2])
}
当我们尝试在数组中移动一个元素,这会导致数组的一部分内容不再被数组拥有,是不可预算的行为。因此是是不被允许的。
fn main() {
let array = ["bob".to_string(), "alice".to_string()];
let user = array[1];
}
修改方法就是使用借用
fn main() {
let array = ["bob".to_string(), "alice".to_string()];
let user = &array[1];
}
切片
一个切片是对数组一部分连续子元素构成的引用。语法如下
&arrayname[startIndex..endIndex]
示例
fn main() {
let array = [1,2,3,4,5,6];
println!("{:?}", &array[..4]);
println!("{:?}", &array[1..]);
println!("{:?}", &array[1..4]);
println!("{:?}", &array[1..=4]);
}
数组的比较
数组只实现了用于比较的PartialEq trait,只能比较==或!=。
两个数组比较必须满足以下规则
- 必须是相同类型元素。
- 两个数组具有相同数量的元素
- 数组里的元素类型本身必须实现 PartialEq,否则没法比较。
fn main() {
let array_1 = [1,2,3,4];
let array_2 = [1,2,3];
let array_3 = [1,2,3,5];
let array_4 = [1,2,3,4];
println!("{}",array_1==array_3);
println!("{}",array_1==array_4);
}
迭代
数组实现了IntoIterator trait,可以使用for-in迭代,for-in会自动调用数组的into_iter方法转换成迭代器。
fn main() {
let array = [1, 2, 3, 4, 5, 6, 7, 8, 9];
// 不包含索引
for item in array {
println!("{}",item);
}
// 包含索引
for item in array.iter().enumerate() {
println!("{:?}", item);
}
}
隐式转换
有时我们需要将数组转换为切片,或者将切片转换为数组。在Rust中,从数组到切片是隐式的,而从切片转换到数组不是隐式的。
你可以在切片上调用try_into方法,这会返回一个Result<T, E>
类型。如果成功,转换成的数组或包含在Ok()内,否则则返回一个Error<E>
错误。
数组类型需要手动声明,并且声明数组类型需要与原切片的长度相同。
fn main() {
let slice = &[1,2,3,4][1..3];
let array:[i32; 2] = slice.try_into().unwrap();
println!("{:?}", array);
}
向量
向量是动态数组,与数组不同的区别是,向量可以动态地伸长和收缩。
- 由于向量的大小是动态的,它在编译时无法确定大小,因此向量本体不能驻留在栈上。
- 向量有一个底层数组,元素值存储在其中。这个底层数组是在堆上分配的。
向量有三个字段 - 当前向量的大小
- 指向堆区底层数组的指针
- 底层数组的容量
这些字段不能直接访问,但可以通过调用方法进行访问。
向量创建时会分配底层数组,当向量的绑定从内存中移除时,底层数组也会被释放。
容量是向量当前的大小,长度是向量存储的实际元素的数量。
当长度超出容量时,底层数组会被重新分配、复制,并增加容量。
增加容量时,
- 分配了一个更大的底层数组,
- 所有值被复制到新的底层数组中
- 原来的底层数组被释放
- 更新向量的指针和容量。
下图描述了一个常规向量的内存布局
创建向量
通过new方法创建一个空向量
fn main() {
let empty_vec = Vec::<i32>::new();
println!("容量:{} 长度:{}", empty_vec.capacity(), empty_vec.len());
}
使用vec!初始化向量
fn main() {
let vec = vec![1, 2, 3, 4];
println!("{:?}",vec);
}
将数组转换为向量
let vec = [1,2,3,4].to_vec();
将切片转换为向量
fn main() {
let splice = &[1,2,3,5];
let vec = splice.to_vec();
println!("{:?}", vec);
}
向量访问
对于向量的访问,使用get方法是更健全的方案。因为它返回的是一个Option枚举,如果成功,则返回Some<T>
(其中T是索引对应的值),如果失败,则返回None
。
fn main() {
let vec = vec![1,2,3,4];
if let Some(val) = vec.get(5) {
println!("{val}");
} else {
println!("索引值为空")
}
}
迭代元素
你可以想遍历数组那样遍历向量。
fn main() {
let vec = vec![1,2,3,4];
for value in vec {
println!("{value}");
}
}
添加或移除元素
- push() 将一个元素添加到vec末尾,没有返回值
- pop() 移除最后一个元素并返回
Option<T>
,成功则将pop的值作为Some<T>
返回,失败则返回None。 - insert(index, value) 向任何位置(指定位置前面)插入元素,没有返回值。
容量
对于一个向量,容量是其底层数组的大小。当向量的大小要超出当前容量时,底层数组会被重新分配。合理地管理容量能够提高向量的性能。
常用管理容量的方法
- with_capacity 用于设置初始容量
- reserve 增加现有vec的容量
- shrink_to_fit减小vec的容量来节约未使用的内存。
fn main() {
let mut vec = Vec::with_capacity(5);
vec.push(1);
vec.push(3);
vec.push(5);
vec.push(7);
vec.push(9);
println!("当前容量:{} 元素个数: {}",vec.capacity(), vec.len());
vec.reserve(8);
println!("当前容量:{} 元素个数: {}",vec.capacity(), vec.len());
vec.shrink_to_fit();
println!("当前容量:{} 元素个数: {}",vec.capacity(), vec.len());
}
更多推荐
所有评论(0)