数组 Array
数组 (array), 用于存在固定长度的相同数据类型的列表.
#![allow(unused)] fn main() { 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:?}"); }