数组 Array
数组 (array), 用于存在固定长度的相同数据类型的列表.
let arr: [i32; 4] = [1, 1, 2, 3];
其类型声明可以写成:
pub use type Array<T, N> = [T; N];
数组内存是分配在栈空间的, 内存是连续分配的, 它的类型及大小是在编译期间就确定的.
[T; N] 在编译期确定元素类型及个数, 且元素个数不可变; 另外, 数组在编译期就需要初始化.
有两种方法来创建数组:
- 可以显式地指定所有元素的值,
let arr = [1, 2, 3, 4, 5]; - 可以一次性初始化成相同的值,
let arr = [42; 100];会创建有100个元素的数组, 元素的值都是42
看下面的一个示例程序, 用于计算 10000 以内的所有质数:
// See: https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes
fn main() {
const MAX_NUM: usize = 10_000;
const NUM_SQUARED: usize = 100;
let mut primes: [bool; MAX_NUM] = [true; MAX_NUM];
primes[0] = false;
primes[1] = false;
for i in 2..=NUM_SQUARED {
if primes[i] {
for j in ((i * i)..MAX_NUM).step_by(i) {
primes[j] = false;
}
}
}
println!("primes <= {MAX_NUM}: [");
let mut count: i32 = 0;
for (index, is_prime) in primes.iter().enumerate() {
if *is_prime {
print!("{index}, ");
count += 1;
}
if count == 10 {
println!();
count = 0;
}
}
println!("]");
}
数组的内存布局
以下面的代码片段作为示例:
use std::mem::size_of_val;
use std::ptr;
fn main() {
let local_start: i32 = 0x1234;
let arr: [i32; 6] = [1, 1, 2, 3, 5, 8];
let addr: *const [i32; 6] = ptr::addr_of!(arr);
let arr_ref: &[i32] = arr.as_slice();
let addr2: *const i32 = arr.as_ptr();
let local_end: i32 = 0x5678;
assert_eq!(size_of_val(&arr), 24);
assert_eq!(addr as *const (), addr2 as *const ());
assert_eq!(arr_ref.as_ptr(), addr2);
assert!(local_start < local_end);
}
在调试器里查看 arr 的内存, 结果如下图:

看内存里的内容, 可以发现 arr 确实是一个存储相同元素大小 (i32) 连续内存块, 其占用的内存大小为
4 * 6 = 24 24个字节.
其它几个变量都是指针类型, 但里面的指针都指向的是 arr 的内存地址:
addr, 直接调用addr_of!()宏, 返回对象的内存地址, 它不需要创建临时对象arr_ref, 是一个胖指针 (fat pointer), 是一个切片引用&[T], 除了包含 buffer 地址之外, 还存储了切片中元素的个数, 为6个addr2, 通过调用slice::as_ptr()方法, 创建一个切片临时对象, 并返回切片的 buffer 地址
把上面的内存块经过抽像处理后, 可以得到各变量的内存布局图:
另外, arr 直接存储在栈内存. 所以数组占用的空间不能太大, 否则会出现 stack overflow 问题,
linux 平台线程的栈内存默认只有 8MB 的空间:
fn main() {
const ARR_LEN: usize = 1_000_000_000;
let arr = [0i32; ARR_LEN];
println!("arr length: {}", arr.len());
}
这个程序会运行失败, 输出如下错误:
thread 'main' has overflowed its stack
fatal runtime error: stack overflow
Aborted (core dumped)
数组的常用方法
数组的操作方法, 比如 arr.len(), 都是隐式地将它先转换成相应的 切片 slice, 再调用切片提供的方法.
as_slice(),as_mut_slice(), 显式地转换成切片 ([T]), 这样就可以调用切片的方法each_ref(),each_mut(), 转换成新的数组, 新数组中每个元素的值是对当前数组中同一个位置元素的引用
fn main() {
let mut distro_list: [String; 3] = [
"Debian".to_owned(),
"Ubuntu".to_owned(),
"Fedora".to_owned(),
];
let distro_ref: [&mut String; 3] = distro_list.each_mut();
for entry in distro_ref {
entry.push_str(" Linux");
}
println!("distro list: {distro_list:?}");
}