Rust中,最常用的数组有2种,一种是速度很快但是长度固定的array,另一种是可动态增长的但是有性能损耗的Vector;我们称array为数组,Vector为动态数组 这两个数组的关系和String和&str很像,前者是长度固定的字符串切片,后者是可动态增长的字符串

创建数组

Rust中,数组的创建:

fn main(){
	let a = [1,2,3,4,5];
}

由于array的元素类型和大小固定,长度也固定,因此array是存储在栈上,性能非常优秀; 动态数组Vector存储在堆上,因此长度可以动态改变. 例子:需要知道一年中各个月份名称的程序之中,很可能希望使用的是array,因为月份是固定的:

let months = ["January","February","March","April","May","June","July","August","September","October","November","December"];

在一些时候,还需要为数组声明类型:

let a:[i32;5]=[1,2,3,4,5];

这种声明方式也说明了array的类型统一,长度固定 另外也可以这样初始化一个重复出现的N次的值:

let a = [3;5];
访问数组元素

数组是连续存放元素的,因此可以通过index来访问存放的元素:

fn main(){
	let a = [9,8,7,6,5];
	
	let first = a[0];
	let second = a[1];
}
越界访问

使用超出数组范围的索引访问数组元素,bigbang

use std::io;

fn main(){
	let a = [1,2,3,4,5];
	println!("Please enter an array index");
	
	let mut index = String::new();
	//读取控制台的输出
	io::stdin()
		.read_line(&mut index)
		.expect("Failed to read line");
	let index:usize=index
		.trim()
		.parse()
		.expect("Index entered was not a number")
	let element = a[index];
	println!(
		"The value of the element at index {} is {}",
		index,element
	);
}

使用cargo run来运行代码,因为数组只有5个元素,当我们输入5去访问第6个元素,则会访问到不存在的数组元素,最终会panic 当我们尝试使用索引访问元素时,Rust会检查指定的索引是否小于数组长度,如果索引大于等于数组长度,rust会出现panic;这种检查只能在运行时进行,编译器无法在编译的时候知道用户输入什么值 这就是Rust的安全特性之一

数组元素为非基础类型:
let array = [String::from("rust is good!");8];
println!("{:#?}",array);

Error:

前面的基本类型可以直接这样,是因为基本类型的Copy特性,但是复杂类型没有,只能一个个创建:

let array = [String::from("rust is good!"),String::from("rust is good!"),String::from("rust is good!")];

println!("{:#?}",array);

垃圾写法: 正确的写法应该调用std::array::from_fn

let array:[String:8]=std::array::from_fn(|_i| String::from("rust is good!"));
println!("{:#?}",array);
数组切片

Slice允许我们引用集合中的部分连续片段,而不是整个集合,对于数组也是,允许我们引用数组的一部分:

let a:[i32;5]=[1,2,3,4,5];

let slice:&[i32]=&a[1..3];

assert_eq!(slice,&[2,3]);

数组切片的特点:

  • 切片的长度可以和数组不同,并不是固定的,而是取决于你使用时指定的起始和结束位置
  • 创建切片的代价很小,只是对底层数组的一个引用
  • 切片类型[T]拥有不固定的大小,而切片引用类型&[T]则具有固定的大小,因为Rust很多时候都需要固定大小数据类型,因此&[T]更有用,&str字符串切片也同理 下面通过一个综合性使用的例子:
fn main(){
	//编译器自动推导出one的类型
	let one = [1,2,3];
	//显式类型标注
	let two:[u8;3] = [1,2,3];
	let blank1 = [0;3];
	let blank2:[u8;3] = [0;3];
	
	// arrays是一个二维数组,其中每一个元素都是一个数组,元素类型时[u8;3]
	let arrays:[[u8;3];4]=[one,two,blank1,blank2];
	// 借用arrays的元素用作循环中
	for a in &arrays{
		print!("{:?}:",a);
		// 将a变成一个迭代器,用于循环
		// 也可以直接使用 for n in a{} 来循环
		for n in a.iter(){
			print!("\t{} + 10 = {}",n,n+10);
		}
		let mut sum = 0;
		// 0..a.len时一个Rust的语法糖,其实就等于一个数组,元素是从0,1,2一直增加到a.len-1
		for i in 0..a.len(){
			sum+=a[i];
		}
		println!("\t({:?}={})",a,sum);
	}
}

In short:

  • 数组类型容易和数组切片混淆,[T;n]描述一个数组的类型,而[T]描述了一个切片的类型,因为切片是运行期的数据结构,长度无法在编译期得到,因此不能用[T;n]来描述
  • [u8;3]和[u8;4]是不同的类型,数组的长度也是类型的属性
  • 实际开发中,使用最多的是数组切片[T],往往通过引用来使用&[T],因为后者有固定的长度