跳至主要內容

复合类型

LincZero大约 13 分钟

复合类型

结构体 (Struct)

三种类型

结构体( struct )可以由各种不同类型组成。使用 struct 关键字来创建。struct 是 structure 的缩写。 结构体可以作为另一个结构体的字段。结构体是可以嵌套的。

有三种类型:

// 1. 经典的 C 语言风格结构体(C struct)
struct 结构体名称 {
    ...
}

// 2. 单元结构体(unit struct),不带字段,在泛型中很有用。
struct Unit;

// 3. 元组结构体(tuple struct),事实上就是具名元组而已。
struct Pair(String, i32);

下面主要以C风格结构体为例

(1) C风格结构体

定义

struct 结构体名称 {
    字段1: 数据类型,
    字段2: 数据类型,
    ...
}

创建、初始化

其实就是对 结构体中的各个元素进行赋值

let 实例名称 = 结构体名称 {
    field1: value1,
    field2: value2
    ...
};

示例

#[derive(Debug)]
struct Study {
    name: String,
    target: String,
    spend: i32,
}

fn main() {
    let s = Study {
        name: String::from("从0到Go语言微服务架构师"),
        target: String::from("全面掌握Go语言微服务落地,代码级一次性解决微服务和分布式系统。"),
        spend: 3,
    };
    println!("{:?}", s);
}

// 输出
Study { name: "从0到Go语言微服务架构师", target: "全面掌握Go语言微服务落地,代码级一次性解决微服务和分布式系统。", spend: 3 }

访问实例属性

实例名称.属性

示例

println!("{}", s.name); // 输出 从0到Go语言微服务架构师

修改结构体实例

结构体实例默认是不可修改的,如果想修改结构体实例,声明时使用mut关键字。

let mut s2 = Study {
    name: String::from("从0到Go语言微服务架构师"),
    target: String::from("全面掌握Go语言微服务落地,代码级一次性解决微服务和分布式系统。"),
    spend: 3,
};
s2.spend = 7; // 修改
println!("{:?}", s2);

// 输出
Study { name: "从0到Go语言微服务架构师", target: "全面掌握Go语言微服务落地,代码级一次性解决微服务和分布式系统。", spend: 7 }

结构体做参数

fn show(s: Study) {
    println!(
        "name is :{} target is {} spend is{}", s.name, s.target, s.spend
    );
}

结构体作为返回值

fn get_instance(name: String, target: String, spend: i32) -> Study {
    return Study {
        name,
        target,
        spend,
    };
}

let s3 = get_instance(
    String::from("Go语言极简一本通"),
    String::from("学习Go语言语法,并且完成一个单体服务"),
    5,
);
println!("{:?}", s3);

// 输出:
Study { name: "Go语言极简一本通", target: "学习Go语言语法,并且完成一个单体服务", spend: 5 }

结构体方法

普通方法

方法(method)是依附于对象的函数。这些方法通过关键字 self 来访问对象中的数据和其他。方法在 impl 代码块中定义。

与函数的区别

  • 函数:可以直接调用,同一个程序不能出现 2 个相同的函数签名的函数,应为函数不归属任何 owner。
  • 方法:归属某一个 owner,不同的 owner 可以有相同的方法签名。
impl 结构体 {							// "impl" 是 "implement" 的缩写。意思是 “实现”的意思。
    fn 方法名(&self, 参数列表) 返回值 {	// "self" 是 "自己" 的意思。 
        							  // &self 表示当前结构体的实例。&self 也是结构体普通方法固定的第一个参数,其他参数可选。
        // 方法体
    }
}

结构体方法的作用域仅限于结构体内部。

impl Study {
    fn get_spend(&self) -> i32 {
        return self.spend;
    }
}

println!("spend {}", s3.get_spend());
// 输出 spend 5

静态方法

// 定义
fn 方法名(参数: 数据类型, ...) -> 返回值类型 {	// 与普通方法的区别:没有 &self 作为第一个参数
	// 方法体
}

// 调用
结构体名称::方法名(参数列表)

示例

impl Study {
    ...
    fn get_instance_another(name: String, target: String, spend: i32) -> Study {
        return Study {
            name,
            target,
            spend,
        };
    }
}

let s4 = Study::get_instance_another(
    String::from("Go语言极简一本通"),
    String::from("学习Go语言语法,并且完成一个单体服务"),
    5,
);

(2) 单元结构体 (unit type)

unit type 是一个类型,有且仅有一个值:

(),单元类型()类似 c/c++/java 语言中的 void

  • 当一个函数并不需要返回值的时候,其他语言返回void
  • rust 则返回()

但语法层面上,void 仅仅只是一个类型,该类型没有任何值;而单元类型()既是一个类型,同时又是该类型的值。

(3) 元祖结构体

实例化

let pair = (String::from("从0到Go语言微服务架构师"), 1);

访问

访问元组结构体的字段

println!("pair 包含 {:?} and {:?}", pair.0, pair.1);

// 输出:
pair 包含"从0到Go语言微服务架构师" and 1

解构

解构一个元组结构体

let (study, spend) = pair;
println!("pair 包含 {:?} and {:?}", study, spend);

// 输出:内容同上
pair 包含"从0到Go语言微服务架构师" and 1

枚举 (Enum)

枚举 enum 关键字允许创建一个从数个不同取值中选其一的枚举类型(enumeration)。任何一个在 struct 中合法的取值在 enum 中也合法。

在日常生活中很常见。比如:1 年有 12 个月,1 周有 7 天。

枚举的定义

enum 枚举名称{
    variant1,
    variant2,
    ...
}

使用枚举

枚举名称::variant

示例

#[derive(Debug)]			// #[derive(Debug)] 注解的作用,就是让 派生自Debug。
enum RoadMap {
    Go语言极简一本通,
    Go语言微服务架构核心22,0Go语言微服务架构师,
}

fn main() {
	let level = RoadMap::0Go语言微服务架构师;
    println!("level---{:?}", level);
}

Option 枚举

enum Option<T> {
   Some(T),      // 用于返回一个值
   None          // 用于返回 null,用None来代替。
}

Option 枚举经常用在函数中的返回值,它可以表示有返回值,也可以用于表示没有返回值。如果有返回值。

可以使用返回 Some(data),如果函数没有返回值,可以返回 None。

fn getDiscount(price: i32) -> Option<bool> {
    if price > 100 {
        Some(true)
    } else {
        None
    }
}

let p = 666;  	// 输出 Some(true)
// let p=6;		// 输出 None
let result = getDiscount(p);
println!("{:?}", result)

match 语句

判断一个枚举变量的值,唯一操作符就是 match。

fn print_road_map(r:RoadMap){
    match r {
        RoadMap::Go语言极简一本通=>{
            println!("Go语言极简一本通");
        },
        RoadMap::Go语言微服务架构核心22=>{
            println!("Go语言微服务架构核心22讲");
        },
        RoadMap::0Go语言微服务架构师=>{
            println!("从0到Go语言微服务架构师");
        }
    }
}

print_road_map(RoadMap::Go语言极简一本通);			  // 输出 Go语言极简一本通
print_road_map(RoadMap::Go语言微服务架构核心22);	// 输出 Go语言微服务架构核心22讲
print_road_map(RoadMap::0Go语言微服务架构师);		// 输出 从0到Go语言微服务架构师

带数据类型的枚举

(不知道能怎么用)

enum 枚举名称 {
    variant1(数据类型1),
    variant2(数据类型2),
    ...
}

示例

#[derive(Debug)]
enum StudyRoadMap {
    Name(String),
}

let level3 = StudyRoadMap::Name(String::from("从0到Go语言微服务架构师"));
match level3 {
    StudyRoadMap::Name(val) => {
        println!("{:?}", val);
    }
}

//输出 "从0到Go语言微服务架构师"

集合 (Collections) - 未

Rust 语言标准库提供了通用的数据结构的实现。包括:

  • 向量 (Vector)
  • 哈希表( HashMap )
  • 哈希集合( HashSet )

向量 (Vector)

Rust 在标准库中定义了结构体 Vec 用于表示一个向量。向量和数组很相似,只是数组长度是编译时就确定了,定义后就不能改变了,那要么改数组,让他支持可变长度,显然 Rust 没有这么做,它用向量这个数据结构,也是在内存中开辟一段连续的内存空间来存储元素。

特点:

  • 向量中的元素都是相同类型元素的集合。
  • 长度可变,运行时可以增加和减少。
  • 使用索引查找元素。(索引从 0 开始)
  • 添加元素时,添加到向量尾部。
  • 向量的内存在堆上,长度可动态变化。

创建向量

new() 静态方法用于创建一个结构体 Vec 的实例。

let mut 向量的变量名称 = Vec::new();

vec!() 宏来简化向量的创建。

let 向量的变量名称 = vec![val1,val2,...]

向量的使用方法

方法说明
new()创建一个空的向量的实例
push()将某个值 T 添加到向量的末尾
remove()删除并返回指定的下标元素
contains()判断向量是否包含某个值
len()返回向量中的元素个数
let mut v = Vec::new();//调用 Vec 结构的 new() 静态方法来创建向量。
v.push("Go语言极简一本通");       //通过push方法添加元素数据。并且追加到向量尾部。
v.push("Go语言微服务核心架构22讲");
v.push("从0到Go语言微服务架构师");
println!("{:?}",v);
println!("len :{}",v.len()); // 通过len方法获取向量中的元素个数。


let mut v2 = vec!["Go语言极简一本通","Go语言微服务核心架构22讲","从0到Go语言微服务架构师"];
// 通过vect!宏创建向量时,向量的数据类型由第一个元素自动推断出来。
println!("{:?}",v2);

let x=v2.remove(0);
// remove()方法移除并返回向量中指定的下标索引处的元素,将其后面的所有元素移到向左移动一位。
println!("{}",x); //输出 Go语言极简一本通
println!("{:?}",v2);//输出 ["Go语言微服务核心架构22讲", "从0到Go语言微服务架构师"]

//contains() 用于判断向量是否包含某个值。如果值在向量中存在则返回 true,否则返回 false。
if v.contains(&"从0到Go语言微服务架构师"){
   println!("找到了")
}

//访问向量中的某个元素,使用索引
let y = v[0];
println!("{}",y); //输出 Go语言极简一本通

//遍历向量
for item in v {
   println!("充电项目: {}", item);
}
//输出
充电项目: Go语言极简一本通
充电项目: Go语言微服务核心架构22讲
充电项目:0Go语言微服务架构师

哈希表 (HashMap)

HashMap 就是键值对集合。键是不能重复的,值是可以重复的。

使用 HashMap 结构体之前需要显式导入 std::collections 模块。

创建 HashMap

使用 new()方法来创建。

let mut 变量名称 = HashMap::new();

这个哈希表只有当我们添加了元素之后才能正常使用。因为现在还没指定的数据类型。

方法说明
insert()插入/更新一个键值对到哈希表中,如果数据已经存在则返回旧值,如果不存在则返回 None
len()返回哈希表中键值对的个数
get()根据键从哈希表中获取相应的值
iter()返回哈希表键值对的无序迭代器,迭代器元素类型为 (&’a K, &’a V)
contains_key如果哈希表中存在指定的键则返回 true 否则返回 false
remove()从哈希表中删除并返回指定的键值对
use std::collections::HashMap;

let mut process = HashMap::new();
process.insert("Go语言极简一本通", 1);
process.insert("Go语言微服务核心架构22讲", 2);
process.insert("从0到Go语言微服务架构师", 3);

println!("{:?}", process);
//输出 {"Go语言极简一本通": 1, "Go语言微服务核心架构22讲": 2, "从0到Go语言微服务架构师": 3}
println!("len {}",menu.len());
//输出  3


// get() 方法用于根据键从哈希表中获取相应的值。
match process.get(&"从0到Go语言微服务架构师"){
   Some(v)=>{
      println!("HashMap v:{}", v);
   }
   None=>{
      println!("找不到");
  }
}
//输出 HashMap v:3

//迭代哈希表 iter()
for (k, v) in process.iter() {
   println!("k: {} v: {}", k, v);
}
//输出
k: Go语言微服务核心架构22讲 v: 2
k:0Go语言微服务架构师 v: 3
k: Go语言极简一本通 v: 1

// contains_key() 方法用于判断哈希表中是否包含指定的 键值对。如果包含指定的键,那么会返回相应的值的引用,否则返回 None。
if process.contains_key(&"Go语言极简一本通") {
   println!("找到了");
}
//输出 找到了

// remove() 用于从哈希表中删除指定的键值对。如果键值对存在则返回删除的键值对,返回的数据格式为 (&'a K, &'a V)。如果键值对不存在则返回 None

let x=process.remove(&"Go语言极简一本通");
println!("{:?}",x);
println!("{:?}",process);
//输出
Some(1)
{"Go语言微服务核心架构22讲": 2, "从0到Go语言微服务架构师": 3}

哈希集合 (HashSet)

Hashset 是相同数据类型的集合,它是没有重复值的。如果集合中已经存在相同的值,则会插入失败。

创建 Hashset

let mut 变量名称 = HashSet::new();

常用方法如下
方法 	描述
insert() 	插入一个值到集合中 如果集合已经存在值则插入失败
len() 	返回集合中的元素个数
get() 	根据指定的值获取集合中相应值的一个引用
iter() 	返回集合中所有元素组成的无序迭代器 迭代器元素的类型为 &'a T
contains_key 	判断集合是否包含指定的值
remove() 	从结合中删除指定的值

insert() 用于插入一个值到集合中。

let mut studySet = HashSet::new();
studySet.insert("Go语言极简一本通");
studySet.insert("Go语言微服务核心架构22讲");
studySet.insert("从0到Go语言微服务架构师");
println!("{:?}", studySet);
//输出 {"从0到Go语言微服务架构师", "Go语言微服务核心架构22讲", "Go语言极简一本通"}

studySet.insert("从0到Go语言微服务架构师");
println!("{:?}", studySet);
//输出 {"从0到Go语言微服务架构师", "Go语言微服务核心架构22讲", "Go语言极简一本通"}

len() 方法集合中元素的个数。

println!("len:{}",studySet.len());//输出 len:3

iter() 方法用于返回集合中所有元素组成的无序迭代器。

for item in studySet.iter(){
    println!("hashSet-充电项目: {}", item);
}
//输出
hashSet-充电项目: Go语言极简一本通
hashSet-充电项目: Go语言微服务核心架构22讲
hashSet-充电项目:0Go语言微服务架构师

get() 方法用于获取集合中指定值的一个引用。

match studySet.get("从0到Go语言微服务架构师") {
    None => {
        println!("没找到");
    }
    Some(data) => {
        println!("studySet中找到:{}",data);
    }
}
//输出 studySet中找到:从0到Go语言微服务架构师

contains() 方法用于判断集合是否包含指定的值。

if studySet.contains("Go语言微服务核心架构22讲"){
    println!("包含 Go语言微服务核心架构22讲")
}
//输出 包含 Go语言微服务核心架构22讲

remove() 方法用于从集合中删除指定的值。如果该值在集合中,则返回 true,如果不存在则返回 false。

studySet.remove("Go语言极简一本通");
println!("{:?}",studySet);//输出 {"Go语言微服务核心架构22讲", "从0到Go语言微服务架构师"}

studySet.remove("golang");
println!("{:?}",studySet);//输出 {"Go语言微服务核心架构22讲", "从0到Go语言微服务架构师"}