Rust容器类型
Rust容器类型
元组 (tuple)
性质
Tuple 元组是一个 复合类型 ,可以存储多个不同类型的数据。 Rust 支持元组 tuple 类型。元组使用括号 () 来构造(construct)。函数可以使用元组来返回多个值,因为元组可以拥有任意多个值。
长度/类型:
- 元组是定长的。一旦定义,就不能再增长或缩小,长度是固定的。元组的下标从 0 开始
- 可以包含不同的类型
- Rust元组 (定长、不同类型)
- 不是C数组(传统数组定长、同类型)
- 声明语法上像Python的元组,也是小括号包起来(变长、不同类型)
- 但其实更类似于C的结构体(定长、不同类型)
- 另外也很像是各语言函数参数的特性(也是定长、不同类型)
内存:字符串对象在堆中,而元组在栈中
应用场景
元组在函数返回值场景很常用,例如下面的代码,可以使用元组返回多个值:
fn main() {
let s1 = String::from("hello");
let (s2, len) = calculate_length(s1);
println!("The length of '{}' is {}.", s2, len);
}
fn calculate_length(s: String) -> (String, usize) {
let length = s.len(); // len() 返回字符串的长度
(s, length)
}
返回多个返回值很方便。像python也有元组和类似的用法
而像Cpp等旧语言很多就不行了,只能返回一个结果体,然后往结果体里去找里面对应key的值,写起来没那么简洁
方法 - 特殊函数
定义元组
let tuple变量名称:(数据类型1,数据类型2,...) = (数据1,数据2,...);
let tuple变量名称 = (数据1,数据2,...); // 自动推断类型
** 注:tuple 使用一对小括号 () 把所有元素放在一起,元素之间使用逗号 , 分隔。如果显式指定了元组的数据类型,那么数据类型的个数必须和元组的个数相同,否则会报错。**
fn main() {
let tup: (i32, f64, u8) = (500, 6.4, 1);
let t:(&str, &str) = ("hello", "world");
println!("{:?}", t);
}
模式匹配
fn main() {
let tup = (500, 6.4, 1);
let (x, y, z) = tup;
println!("The value of y is: {}", y);
}
方法 - 增删改查
访问元素
// 原型: 元组变量.索引数字
let t:(&str, &str) = ("Go语言极简一本通", "掌握Go语言语法,并且可以完成单体服务应用");
println!("{}", t.0); // 输出 Go语言极简一本通
println!("{}", t.1); // 输出 掌握Go语言语法,并且可以完成单体服务应用
元组作参数
元组一般是值传递
// 原型: fn 函数名称(tuple参数名称:(&str, i32)) {}
fn show_tuple(tuple:(&str, &str)){
println!("{:?}",tuple);
}
let t:(&str, &str) = ("Go语言极简一本通", "掌握Go语言语法,并且可以完成单体服务应用");
show_tuple(t); // 输出 ("Go语言极简一本通", "掌握Go语言语法,并且可以完成单体服务应用")
元组解构
元组 (tuple) 解构 就是在 tuple 中的每一个元素按照顺序一个一个赋值给变量。使用 = ,让右边的 tuple 按照顺序给等号左变的变量一个一个赋值。
这点有点像Python
let (book, target) = t;
println!("{}", book); // 输出 Go语言极简一本通
println!("{}", target); // 输出 掌握Go语言语法,并且可以完成单体服务应用
数组
两种类型
在 Rust 中,最常用的数组有两种,第一种是速度很快但是长度固定的 array
,第二种是可动态增长的但是有性能损耗的 Vector
,在本书中,我们称 array
为数组,Vector
为动态数组。
这两个数组的关系跟 &str
与 String
的关系很像,前者是长度固定的字符串切片,后者是可动态增长的字符串。
本章中,先说数组 array
定义
数组 是用来存储一系列数据,拥有相同类型 T 的对象的集合,在内存中是连续存储的。使用中括号 []
来创建,且它们的大小在编译时会被确定。数组下标是从0 开始。数组是在栈中分配的,数组可以自动被借用成为 切片(slice)。
声明和初始化
let 变量名:[数据类型; 数组长度] = [值1, 值2, 值3, ...]; // 一般写法
let arr1:[&str; 3] = ["Go语言极简一本通", "Go语言微服务架构核心22讲", "从0到Go语言微服务架构师"];
let 变量名 = [值1, 值2, 值3, ...]; // 类型推导
let arr2 = ["Go语言极简一本通", "Go语言微服务架构核心22讲", "从0到Go语言微服务架构师"];
let 变量名:[数据类型; 数组长度] = [默认值, 数组长度]; // 快速初始化,有点类似Cpp的大括号初始化或python的?解析表达式数组
let arr3:[&str; 3] = [""; 3];
方法
获取数组长度 len()
print!("{}", arr1.len());
遍历数组
for item in arr1 {
print!("充电科目: {}\n",item);
}
// 输出
充电科目: Go语言极简一本通
充电科目: Go语言微服务架构核心22讲
充电科目: 从0到Go语言微服务架构师
for item in arr1.iter(){
print!("已参加的充电科目: {}\n",item);
}
// 输出
已参加的充电科目: Go语言极简一本通
已参加的充电科目: Go语言微服务架构核心22讲
已参加的充电科目: 从0到Go语言微服务架构师
如果修改一个不可变数组,报错如下:
arr2[0] = "";
error[E0594]: cannot assign to `arr2[_]`, as `arr2` is not declared as mutable
如果想修改这个错误,声明数组的时候,添加 mut 关键字
数组做参数
值传递 传递一个数组的副本,副本的修改,不会影响原数组。
fn show_arr(arr:[&str;3]){
let l = arr.len();
for i in 0..l {
if i==0 {
arr[0] = ""
}
println!("充电科目: {}",arr[i]);
}
}
let mut arr2 = ["Go语言极简一本通", "Go语言微服务架构核心22讲", "从0到Go语言微服务架构师"];
print!("{:?}\n", arr2);
show_arr(arr2);
print!("{:?}\n", arr2);
// 输出
["Go语言极简一本通", "Go语言微服务架构核心22讲", "从0到Go语言微服务架构师"]
充电科目:
充电科目: Go语言微服务架构核心22讲
充电科目: 从0到Go语言微服务架构师
["Go语言极简一本通", "Go语言微服务架构核心22讲", "从0到Go语言微服务架构师"]
引用传递 传递内存的地址给函数,修改数组的任何值都会修改原来的数组。
fn modify_arr(arr:&mut [&str;3]){
let l = arr.len();
for i in 0..l {
arr[i]="";
}
}
let mut arr3=["Go语言极简一本通","Go语言微服务架构核心22讲","从0到Go语言微服务架构师"];
print!("{:?}\n",arr3);
modify_arr(&mut arr3);
print!("{:?}\n",arr3);
// 输出
["Go语言极简一本通", "Go语言微服务架构核心22讲", "从0到Go语言微服务架构师"]
["", "", ""]
切片
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
字符串切片也同理
Vec动态数组
// 声明一个 fields 变量,类型是 Vec
// Vec 是 vector 的缩写,是一个可伸缩的集合类型,可以认为是一个动态数组
// <_>表示 Vec 中的元素类型由编译器自行推断,在很多场景下,都会帮我们省却不少功夫
let fields: Vec<_> = record
.split(',')
.map(|field| field.trim())
.collect();
if cfg!(debug_assertions) {
// 输出到标准错误输出
eprintln!("debug: {:?} -> {:?}",
record, fields);
}
序列
Rust 提供了一个非常简洁的方式,用来生成连续的数值,例如 1..5
,生成从 1 到 4 的连续数字,不包含 5 ;1..=5
,生成从 1 到 5 的连续数字,包含 5,它的用途很简单,常常用于循环中:
for i in 1..=5 {
println!("{}",i);
}
(这个有点像 python 的列表解析式,那个同样是一个很好用的特性甚至更灵活)
切片 (slice)
切片并不是 Rust 独有的概念,在 Go 语言中就非常流行,它允许你引用集合中部分连续的元素序列,而不是引用整个集合。
切片是对集合的部分引用,因此不仅仅字符串有切片,其它集合类型也有
字符串切片
对于字符串而言,切片就是对 String
类型中某一部分的引用,它看起来像这样:
let s = String::from("hello world");
let hello = &s[0..5];
let world = &s[6..11];
hello
没有引用整个 String s
,而是引用了 s
的一部分内容,通过 [0..5]
的方式来指定。
注意:字符串字面量是切片,类型是 &str