当前位置: 首页 > news >正文

Rust程序语言设计(5-8)

五、使用结构体来组织相关联的数据

struct ,结构体

  • 自定义的数据类型
  • 命名相关联的值,打包 -> 有意义的组合
(1)定义和实例化 struct
定义 struct
  • 使用 struct 关键字,并为整个 struct 命名
  • 花括号内,为所有 字段 定义名称和类型
    例:
struct User {username:String,email:String,age:i32,active:bool,}
实例化 struct

创建 struct 实例

  • 为每个字段指定具体值
  • 无需按照声明顺序
  • 无 mut 不可更改
struct User {username:String,email:String,age:i32,active:bool,}
fn main() {let user1 = User {   //无 mut ,不可更改username:String::from("MOON"),email:String::from("example@123.com"),age:19,active:true,};
}

取出一个值:

struct User {username:String,email:String,age:i32,active:bool,}
fn main() {let mut user1 = User {username:String::from("MOON"),email:String::from("example@123.com"),age:19,active:true,};println!("{}",user1.email);
}

==注:==一旦 struct 的实例是可变的,那么实例中所有的字段都是可变的

struct 作为函数返回值

例:

struct User {username:String,email:String,age:i32,active:bool,
}fn main() {let user1 = demo(String::from("admin"),String::from("example@123.com"),19,true);// 传入 &str ,报错,所以转换成 string 类型println!("{}",user1.email);
}fn demo(username:String,email:String,age:i32,active:bool) -> User {User {username,email,age,active,   //简写 -> active:active,}
}
struct 更新语法

基于某个 struct 实例来创建一个新的实例时使用

//如,仅更换几个参数
let user2 = User {email: String::from("example@123.com"),username: String::from("admin"),active: user1.active,age: user1.age,
};//使用 struct 更新语法:
let user2 = User {email: String::from("example@123.com"),username: String::from("admin"),..user1
};
Tuple struct

概念:定义类似 tuplestruct ,叫做 tuple struct

  • Tuple struct 整体有名,内部元素没有名
  • 适用于给 tuple 起名,并使它不同于其他 tuple
    定义 tuple structstruct [名字] (元素类型,元素类型,元素类型,…)
    例如:
struct Color(i32,i32,i32);
struct Point(i32,i32,i32);
let black = Color(0,0,0);
let origin = Point(0,0,0);

注: blackorigin 是不同的类型,是不同的 tuple struct 的实例。

Unit-Like Struct

概念:可以定义无字段的 struct,叫做 Unit-Like struct
适用于需要在某个类型上实现某个 trait,但在里面没有想要存储的数据

struct 数据的所有权
struct User {username: String,email: String,age: i32,active: bool,
}

这里字段使用了 String 而不是 &str

  • struct 实例拥有其所有的数据
  • 只要 struct 实例是有效的,那么里面的字段数据也是有效的
    struct 里也可以存放引用,但这需要使用生命周
  • 生命周期保证只要 struct 实例是有效的,那么里面的引用也是有效的
  • 如果 struct 里面存储引用,而不使用生命周期,就会报错
(2)struct 示例
计算长方形面积

基本实现:

fn main() {let w = 30;let l = 50;println!("长方形的面积为:{}",area(w,l));
}fn area(width: u32, length: u32) -> u32 {width * length
}

元组:

fn main() {let rect = (30,50);println!("长方形的面积为:{}",area(rect));
}fn area(dim:(u32, u32)) -> u32 {  //元素没有名字,可读性差dim.0 * dim.1
}

结合 struct 增强代码可读性

struct Rectangle {width: u32,length: u32,
}fn main() {let rect = &Rectangle {width: (30),length: (50),};println!("长方形的面积为:{}",area(&rect));// 传递 &rect 引用,不会获取到所有权,以便后续使用
}fn area(rect: &Rectangle) -> u32 {rect.width * rect.length
}

验证所有权:

struct Rectangle {width: u32,length: u32,
}fn main() {let rect = &Rectangle {width: (30),length: (50),};println!("长方形的面积为:{}",area(&rect));// 传递 &rect 引用,不会获取到所有权,以便后续使用println!("{}",rect.weigth);// 单个元素调用println!("如想输出全部元素{:?}",rect);// 这种输出需要添加 debug 在开头  // #[derive(Debug)]
}fn area(rect: &Rectangle) -> u32 {rect.width * rect.length
}

添加debug:

#[derive(Debug)]struct Rectangle {width: u32,length: u32,
}fn main() {let rect = &Rectangle {width: (30),length: (50),};println!("长方形的面积为:{}",area(&rect));println!("如想输出全部元素{:?}",rect);// 这种输出需要添加 debug 在开头  // #[derive(Debug)]
}fn area(rect: &Rectangle) -> u32 {rect.width * rect.length
}

运行结果:
![[Pasted image 20251007113236.png]]

{:?} -> {:#?} 后再输出:
![[Pasted image 20251007113414.png]]

(3)struct 方法

方法和函数类似:fn关键字、名称、参数、返回值
方法与函数不同之处:

  • 方法是在 struct(或 enumtrait 对象)的上下文中定义
  • 第一个参数是 self ,表示方法被调用的 struct 实例
    定义方法:
impl Rectangle {fn area(&self) -> u32 {self.width * self.length}
}

主函数中调用:

fn main() {let rect = &Rectangle {width: (30),length: (50),};println!("长方形的面积为:{}",rect.area());    // rect.方法名()println!("{:#?}",rect);
}
定义方法

impl 块里定义方法
方法的第一个参数可以是 &self,也可以获得其所有权可变借用

方法调用的运算符

C/C++:object -> something() 和 (*object).something()
Rust 没有 -> 运算符
Rust 会自动引用或解引用

  • 在调用方法时就会发生这种行为
    在调用方法时,Rust 根据情况自动添加 &&mut*,以便 object 可以匹配方法的签名
    例如:
p1.distance(&p2);
(&p1).distance(&p2);
//两者表达效果相同

判断长方形能否包含另一个长方形

#[derive(Debug)]struct Rectangle {width: u32,length: u32,
}impl Rectangle {fn area(&self) -> u32 {self.width * self.length}fn can_hold(&self, other: &Rectangle) -> bool {self.width > other.width && self.length > other.length}
}fn main() {let rect1 = &Rectangle {width: (30),length: (50),};let rect2 = &Rectangle {width: (10),length: (40),};let rect3 = &Rectangle {width: (35),length: (55),};println!("{}",rect1.can_hold(&rect2));//判断rect1 能否包含 rect2println!("{}",rect1.can_hold(&rect3));//判断rect1 能否包含 rect2
}
关联函数

概念:可以在 ipml 块里定义不把 self 作为第一参数的函数,叫做关联函数

  • 例如:String::from()
    关联函数通常用于构造器
    例:正方形
fn square(size: u32) -> Rectangle {Rectangle {width: size,length: size,}
}

输出正方形面积:

let s = Rectangle::square(50);
println!("正方形的面积是:{}",s.area());

:: 符号:

  • 关联函数
  • 模块创建的命名空间
多个 impl 块

每个 struct 允许拥有多个 impl

六、枚举与模式匹配

(1)定义枚举
定义枚举

IP地址:IPv4、IPv6

enum IpAddrKind {V4,V6,
}fn main() {let four = IpAddrKind::V4;let six = IpAddrKind::V6;route(four);route(six);route(IpAddrKind::V6);
}fn route(ip_kind: IpAddrKind) {}
将数据附加到枚举的变体中

优点:

  • 不需要额外使用 struct
  • 每个变体可以拥有不同的类型以及关联的数据量
    例:
enum IpAddrKind {V4,V6,
}struct IpAddr {kind: IpAddrKind,address: String,
}fn main() {let home = IpAddr {kind: IpAddrKind::V4,address: String::from("127.0.0.1"),};let loopback = IpAddr {kind: IpAddrKind::V6,address: String::from("::1"),};
}

更改为:

enum IpAddrKind {V4(u8, u8, u8, u8),V6(String),
}fn main() {let home = IpAddrKind::V4((127), (0), (0), (1));let loopback = IpAddrKind::V6((::1));
}
为枚举定义方法 (impl)
enum Message {Quit,Move {x: i32, y: i32},Write(String),ChangeColor(i32, i32, i32),
}impl Message {fn call(&self) {}
}fn main() {let q = Message::Quit;let m = Message::Move { x: (12), y: (24) };let w = Message::Write(("Hello"));let c = Message::ChangeColor((0), (255), (255));m.call();
}
(2)Option枚举
  • 定义于标准库中
  • Prelude (预导入模型 )
  • 描述了:某个值可能存在(某种类型)或不存在的情况
Rust 没有 Null

其他语言:

  • Null 是一个值,它表示"没有值"
  • 一个变量可以处于两种状态:空值(null)、非空
    Null的问题在于:如果使用 Null 值像非 Null 值一样,会引发错误
    概念:因某种原因而变为无效或缺失的值
Rust中类似 Null 概念的枚举-Option<T>

标准库中的定义:

enum Option<T> {Some(T),None
}

包含于 预导入模块 中:

  • Option<T>
  • Some(T)
  • None
    例:
fn main() {let some_number = Some(5);let some_string = Some("A String");let absent_number: Option<i32> = None;
}

==注:==Option<T> != T

Option<T> 相较于 Null
  • Option<T> 和 T 是不同的类型
  • 若想使用 Option<T> 中的 T 必须先转换成 T
(3)控制流运算符 match
  • 允许一个值与一系列模式进行匹配,并执行匹配的模式对应的代码
  • 模式可以是字面值、变量名、通配符等
enum Coin {Penny,Nickel,Dime,Quarter,
}fn Value_in_cents(coin: Coin) -> u8 {match coin {Coin::Penny => 1,Coin::Nickel => 5,Coin::Dime => 10,Coin::Quarter => 25,}
}fn main() {}
绑定值的模式

匹配的分支可以绑定到被匹配对象的部分值

  • 因此,可以从 enum 中提取值
#[derive(Debug)]enum UsState {Alabama,Alaska,
}
enum Coin {Penny,Nickel,Dime,Quarter(UsState),
}fn Value_in_cents(coin: Coin) -> u8 {match coin {Coin::Penny => 1,Coin::Nickel => 5,Coin::Dime => 10,Coin::Quarter(state) => {println!("State quarter from {:?}", state);25}}
}fn main() {let c = Coin::Quarter(UsState::Alaska);println!("{}", Value_in_cents(c));
}
匹配 Option<T>
fn main() {let five = Some(5);let six = plus_one(five);let none = plus_one(None);
}fn plus_one(x: Option<i32>) -> Option<i32> {match x {None => None,Some(i) => Some(i + 1),}
}
match 匹配必须穷举所有可能

假设没有列出 None 可能性

fn main() {let five = Some(5);let six = plus_one(five);let none = plus_one(None);
}fn plus_one(x: Option<i32>) -> Option<i32> {match x {//None => NoneSome(i) => Some(i + 1),}
}

运行结果
![[Pasted image 20251007191705.png]]

如,没有全部列出的必要的情况,可用 _ 代替其余值,必须在最后

1 => println!(1),
2 => println!(2),
3 => println!(3),
_ => (),
(4)简单控制流 if let
  • 处理只关心一种匹配而忽略其它匹配的情况
  • 更少的代码、缩进、以及模板
  • 放弃了穷举
    例:对比
fn main() {let v = Some(0u8);match v {Some(3) => println!("three"),_ => println!("others"),}if let Some(3) = v {println!("three");}
}
if let 搭配 else
fn main() {let v = Some(0u8);match v {Some(3) => println!("three"),_ => (),}if let Some(3) = v {println!("three");} else {println!("others");}
}

七、使用包、单元包及模块管理项目

(1)Package、Crate、Module
Rust 的代码组织

代码组织主要包括:

  • 哪些细节可以暴露,哪些细节是私有的
  • 作用域内哪些名称有效
    模块系统:
  • Package(包):Cargo 的特性,构建、测试、共享 crate
  • Crate(单元包):一个模块树,可以产生一个 library 或可执行文件
  • Module(模块):控制代码的组织、作用域、私有路径
  • Path(路径):为 struct、function或module等项命名的方式
Package 和 Crate

Crate 的类型:

  • binary
    -library
    Crate Root
  • 源代码文件
  • Rust 编译器从这里开始,组成 Crate 的根 Module
    Package
  • 包含 1 个 Cargo.toml,描述了如何构建这些 Crates
  • 只能包含 0-1 个 library crate
  • 可以包含任意数量的 binary crate
  • 必须至少包含一个 crate(library 或 binary)
创建

cargo new project
![[Pasted image 20251008105629.png]]

Cargo.toml(配置文件)

[package]
name = "project"
version = "0.1.0"
edition = "2024"[dependencies]

main.rc(源代码文件、入口文件)

Cargo 的惯例

src/main.rs

  • binary cratecrate root
  • crate 名与 package 名相同
    src.lib.rs
  • package 包含一个 library crate
  • library cratecrate root
  • crate 名与 package 名相同
    一个 Package 可以同时包含 src/main.rssrc/lib.rs
  • 一个 binary crate,一个 library crate
  • 名称与 package 名相同
    一个 Package 可以有多个 binary crate
  • 文件放在 src/bin
  • 每个文件是单独的 binary crate
crate 的作用

将相关功能组合到一个作用域内,便于项目间进行共享

  • 防止冲突
    如:rand crate,访问它的功能需要通过它的名字:rand
定义 module 来控制作用域和私有性

Module

  • 在一个 crate 内,将代码进行分组
  • 增加可读性,易于复用
  • 控制项目(item)的私有性
    建立 module
  • mod 关键字
  • 可嵌套
  • 可包含其他项(structenum常量trait函数)的定义
    例:
mod front_of_house {mod hosting {fn add_to_eaitlist() {}fn seat_at_table() {}}mod srving {fn take_order() {}fn serve_order() {}fn take_payment() {}}
}
Module

src/main.rssrc/lib.rs 叫做 crate roots

  • 这两个文件(任意一个)的内容形成了名为 crate 的模块,位于整个模块树的根部
 crate└── front_of_house├── hosting│   ├── add_to_waitlist│   └── seat_at_table└── serving├── serve_order├── take_order└── take_payment
(2)路径

为了在 Rust 的模块中找到某个条目,需要使用路径
路径的两种形式:

  • 绝对路径:从 crate root 开始,使用 crate 名 或 字面值 crate
  • 相对路径:从当前模块开始,使用 selfsuper 或当前模块标识符
    路径至少由一个标识符组成,标识符之间使用 ::
    例:
mod fount_of_house {mod hosting{fn add_to_waitlist() {}}
}pub fn eat_at_restaurant() {crate::front_of_house::hosting::add_to_waitlist();front_of_house::hosting::add_to_waitlist();
}

注:该例子无法正常运行,存在调用私有

私有边界
  • 模块不仅可以组织代码,还可以定义私有边界
  • 如果想把 函数 或 struct 等设为私有,可以将它放到某个模块中
  • Rust 中所有的条目,(函数、方法、struct、enum、模块、常量)默认是私有的
  • 父级模块无法访问子模块中的 私有条目
  • 子模块里可以使用所有祖先模块中的条目
pub 关键字

将某些条目标记为公共的

mod fount_of_house {pub mod hosting{pub fn add_to_waitlist() {}}
}pub fn eat_at_restaurant() {crate::front_of_house::hosting::add_to_waitlist();//正常运行front_of_house::hosting::add_to_waitlist();
}
(3)super、pub struct、enum
super 关键字

super:用于访问父级目录,类似文件系统中的 …

fn serve_order() {}mod back_of_house {fn fix_incorrect_order() {cook_order();super::serve_order();}fn cook_order() {}
}
pub struct

声明 struct 为公共的
例:

mod back_of_house {pub struct Breakfast{pub xxx: String,  //公有yyy: String,    //私有}
}

pub struct:

  • struct 是公共的
  • struct 的字段默认是私有的
    struct 的字段需要单独设置 pub 来变为公有
pub enum

pub 放在 enum 前:

  • enum 是公共的
  • enum 的变体也是公共的
    例:
mod back_of_house {pub enum Appetizer {Soup,Salad,}
}
(4)use 关键字

可以使用 use 关键字将路径导入到作用域内

  • 仍遵循私有性规则
mod front_of_house {pub mod hosting {pub fn add_to_waitlist() {}}
}use crate::front_of_house::hosting;pub fn eat_at_restaurant() {hosting::add_to_waitlist();hosting::add_to_waitlist();hosting::add_to_waitlist();
}

使用 use 指定相对路径

use 的习惯用法
  • 函数:将函数的父级模块引入作用域(指定到父级)
  • struct、enum,其他:指定完整路径(指定到本身)
    例:(哈希表)
use std::collections::HashMap;fn main() {let mut map = HashMap::new();map.insert(1,2);
}
  • 同名条目,指定到父级
use std::fmt;
use std::io;fn f1() -> fmt::Result {}
fn f2() -> io::Result {}fn main() {}
as 关键字

as 关键字可以为引入的路径指定本地的别名

use std::fmt::Result;
use std::io::Result as IoResult;fn f1() -> Result {}
fn f2() -> IoResult {}fn main() {}
使用 pub use 重新导出名称
  • 使用 use 将路径导入到作用域后,该名称在此作用域是 私有
    例:
mod front_of_house {pub mod hosting {pub fn add_to_waitlist() {}}
}pub use crate::front_of_house::hosting;pub fn eat_at_restaurant() {hosting::add_to_waitlist();hosting::add_to_waitlist();hosting::add_to_waitlist();
}

pub use:重导出

  • 将条目引入作用域
  • 该条目可以被外部代码引入到它们的作用域
使用外部包(package)

1、Cargo.toml 添加依赖的包

  • https://crate.io/
    2、use 将特定条目引入作用域
    例:Cargo.toml引入 rand
[package]
name = "project"
version = "0.1.0"
edition = "2024"[dependencies]
rand = "0.5.5"

函数中引用

use rand::Rng;

标准库(std)也被当做外部包

  • 不需要修改 Cargo.toml 来包含 std
  • 需要使用 use 将 std 中的特定条目引入当前作用域
使用嵌套路径清理大量的 use 语句

如果使用同一个包或模块下的多个条目
可使用嵌套路径在同一行内进行多条引入

  • 路径相同部分::{差异部分}
    例:
情况一:
// use std::cmp::Ordering;
// use std::io;use std::{cmp::Ordering, io};情况二:
// use std::io;
// use std::io::Write;use std::io{self, Write};
通配符*

使用 * 可以把路径中所有的公共条目都引入到作用域

use std::collections::*;
(5)模块拆分
将模块内容移动至其他文件

模块定义时。如果模块名后边是“;” ,而不是代码块:

  • Rust 会从与模块同名的文件中加载内容
  • 模块树的结构不会变化

![[Pasted image 20251009192154.png]]

在同目录下找,再次拆分则需要创建文件夹
![[Pasted image 20251009192717.png]]

随着模块逐渐变大,该技术可以将模块的内容移动到其他文件中

八、通用集合类型

(1)Vector
使用 Vector 存储多个值

Vec<T>,叫做 vector

  • 由标准库提供
  • 可存储多个值
  • 只能存储相同类型的数据
  • 值在内存中连续存放
创建 Vector

Vec::new 函数
例:

fn main() {let v: Vec<i32> = Vec::new();
}

使用初始值创建 Vec<T>,使用 vec!

fn main() {let v = vec![1, 2, 3];
}
更新 Vector

Vector 添加元素,使用 push 方法
例:

fn main() {let mut v = Vec::new();v.push(1);v.push(2);v.push(3);v.push(4);
}
删除 Vector

与其他 struct 一致,离开作用域后自动清理
例:

fn main() {let v = vec![1, 2, 3, 4];
}
读取 Vector 的元素

两种方式可以引用 Vector 里的元素

  • 索引
  • get 方法
    例:
fn main() {let v = vec![1, 2, 3, 4, 5];//索引let third: &i32 = &v[2];println!("The third element is {}", third);//getmatch v.get(2) {Some(third) => println!("The third element is {}",third),None => println!("There is no third element"),}
}
索引 vs get 处理访问越界
  • 索引:panic
  • get:返回 None
所有权和借用规则

不能在同一作用域内同时拥有可变和不可变引用

遍历 Vector 中的值

for 循环:
例:

// 不可变
fn main() {let v = vec![100, 32, 57];for i in &v {println!("{}", i);}
}// 可变
fn main() {let mut v = vec![100, 32, 57];for i in &mut v {*i += 50;// *i 解引用	}for i in &v {println!("{}", i);}
}
使用 enum 来存储多种数据类型
  • Enum 的变体可以附加不同类型的数据
  • Enum 的变体定义在同一个 enum 类型下
    例:
enum SpreadsheetCell {Int(i32),Float(f64),Text(String),
}fn main() {let row = vec![SpreadsheetCell::Int(3),SpreadsheetCell::Text(String::from("blue")),SpreadsheetCell::Float(10.12),];
}
(2)String

概念: Byte 的集合,通过一些方法将 byte 解析为文本

字符串是什么

Rust 的核心语法层面,只有一个字符串类型:字符串切片 str(或 &str
字符串切片:对存储在其他地方、UTF-8 编码的字符串的引用

  • 字符串字面值:存储在二进制文件中,也是字符串切片
    String 类型:
  • 来自 标准库 而不是 核心语言
  • 可增长、可修改、可拥有
  • UTF-8
通常所说的字符串

String&str

  • 标准库里用的多
  • UTF-8 编码
Rust提供的其他字符串类型

OsString、OsStr、CString、CStr

  • String vs Str 后缀:拥有或借用的变体
  • 可存储不同编码的文本或在内存中以不同的形式展现
    Library crate (第三方库)针对存储字符串可提供更多的选项
创建字符串(String)

Vec<T> 的操作大部分可用于 String
1、String::new() 函数
例:

fn main() {let mut s = String::new();
}

2、使用初始值来创建 String

  • to_string() 方法,可用于实现了 Display trait 的类型,包括字符串字面值
    例:
fn main() {let data = "initial contents";let s = data.to_string();let s1 = "initial contents".to_string();
}
  • String::from() 函数,从字面值创建 String
    例:
fn main() {let s = String::from("initial contents");
}
更新 String
  • push_str() 方法:把一个字符串切片附加到 String
    例:
fn main() {let mut s = String::from("foo");s.push_str("bar");println!("{}", s);  //&str
}
  • push() 方法:把单个字符附加到 String
    例:
fn main() {let mut s = String::from("lo");s.push('l');
}
  • + 拼接字符串
    例:
fn main() {let s1 = String::from("Hello,");let s2 = String::from("World!");let s3 = s1 + &s2;println!("{}",s3);//拼接后 s1 无法使用,s2 可以正常使用
}
  • format!:连接多个字符串
    例:
fn main() {let s1 = String::from("tic");let s2 = String::from("tac");let s3 = String::from("toe");//let s = s1 + "-" + &s2 + "-" + &s3;let s = format!("{}-{}-{}",s1,s2,s3);println!("{}", s);
}	

String类型不支持索引语法

内部表示

String 是对 Vec<T> 的包装

  • len()方法(长度)
    例:
fn main() {let len = String::from("Hola").len();println!("{}", len);
}
Bytes,Scalar Values,Grapheme Clusters
  • 字节
  • 标量值
  • 字符簇
    例:
let i = "xxxxxx";
i.bytes   //字节
i.chars   //标量值

Rust 不允许对 String 进行索引的最后一个原因:

  • 索引操作应消耗一个常量时间(O(1))
  • String 无法保证:需要遍历所有内容,确定有多少个合法字符
切割 String

使用 []一个范围 来创建字符串的切片
例:

fn main() {let hello = "abcdefghij";let s = &hello[0..4];
}
  • 谨慎使用
  • 如切割时跨越了字符边界,程序就会 panic
  • (b1,b2),(b3,b4),(b5,b6),(b7,b8)
遍历 String 方法

标量值:chars()方法
字节:bytes()方法

注:

Rust 选择将正确处理 String 数据作为所有 Rust 程序的默认行为

  • 程序员必须投入精力处理 UTF-8 数据
    可防止在开发后期处理设计非 ASCII 字符的错误
(3)HashMap
HashMap<K,V>

键值对的形式存储数据,一个键(Key)对应一个值(Value)
Hash 函数:决定如何在内存中存放 KV
使用场景:通过 K(任何类型)来寻找数据,而不是通过索引

创建 HashMap

创建空 HashMap: new() 函数
添加数据:insert() 方法
例:

use std::collections::HashMap;fn main() {// let mut scores: HashMap<String, i32> = HashMap::new();let mut scores = HashMap::new();   //必须声明类型scores.insert(String::from("Blue"), 10);
}

注:

  • HashMap 用的较少,不在 Prelude(预导入) 中,需手动引入
  • 标准库支持少,没有内置 来创建 HashMap
  • 数据存储在 heap
  • 同构中,一个 HashMap中:
  • 所有 K 必须同类型
  • 所有 V 必须同类型
另一种创建方式:collect 方法

在元素类型为 TupleVector 上使用 collect 方法,可以组建一个 HashMap

  • 要求 Tuple 的两个值:一个作为 K,一个作为 V
  • collect 方法可以把数据整合成很多种集合类型,包括 HashMap
    • 返回值需要显示指明类型
      例:
use std::collecctions::HashMap;fn main() {let teams = vec![String::from("Blue"), String::from("Yellow")];let intial_scores = vec![10, 50];let scores: HashMap<_, _> = teams.iter().zip(intial_scores.iter()).collect();// zip() 合成一个元组
}
HashMap 和所有权

对于实现了 Copy trait 的类型(例如 i32),值会被复制到 HashMap
对于拥有所有权的值(例如 String),值会被移动,所有权会转移给 HashMap
例:

use std::collections::HashMap;fn main() {let field_name = String::from("Favorite color");let field_value = String::from("Blue");let mut map = HashMap::new();map.insert(field_name, field_value);//map.insert(&field_name, &field_value);// 没有直接获取所有权,后续可正常使用println!("{}: {}", field_name, field_value);  //所有权移交,此处报错
}

如果将值的引用插入到 HashMap,值本身不会移动

  • HashMap 有效期间,被引用的值必须保持有效
访问 HashMap 中的值

get 方法

  • 参数:K
  • 返回:Option<&V>
    例:
use std::collections::HashMap;fn main() {let main scores = HashMap::new();scores.insert(String::from("Blue"), 10);scores.insert(String::from("Yellow"), 50);let team_name = String::from("Blue");let score = scores.get(&team_name);match score {Some(s) => println!("{}", s),None => println!("team not exist"),}
}
遍历 HashMap

for 循环
例:

use std::collections::HashMap;fn main() {let main scores = HashMap::new();scores.insert(String::from("Blue"), 10);scores.insert(String::from("Yellow"), 50);for (k, v) in %scores {println!("{}: {}", k, v);}
}
更新HashMap<K, V>

HashMap 大小可变
每个 K 同时只能对应一个 V
更新 HashMap 中的数据:
1、K 已经存在,对应一个 V

  • 替换现有的 V
  • 保留现有的 V ,忽略新的 V
  • 合并现有的 V 和新的 V
    2、K 不存在
  • 添加一对 KV
替换现有的 V

HashMap 插入一对 KV,再插入同样的 K,不同的 V,就会替换掉原来的 V
例:

use std::collections::HashMap;fn main() {let mut scores = HashMap::new();scores.insert(String::from("Blue"), 10);scores.insert(String::from("Blue"), 20);println!("{:?}", scores);
}

![[Pasted image 20251010153216.png]]

只在 K 不对应任何值的情况下,插入 V

entry 方法:检查指定的 K 是否对应一个 V

  • 参数为 K
  • 返回 enum Entry:代表值是否存在
    例:
use std::collections::HashMap;fn main() {let mut scores = HashMap::new();scores.insert(String::from("Blue"), 10);scores.entry(String::from("Yellow")).or_insert(50);scores.entry(String::from("Blue")).or_insert(50);println!("{:?}", scores);
}

Entryor_insert() 方法 :

  • 如果 K 存在,返回到对应的 V 的一个可变引用
  • 如果 K 不存在,将方法参数作为 K 的新值插入,返回该值的可变引用
基于现有 V 更新 V

例:

use std::collections::HashMap;fn main() {let text = "Hello world wonderful world";let mut map = HashMap::new();for word in text.split_whitespace() {let count = map.entry(word).or_insert(0);*count += 1;}println!{"{:#?}", map};
}

![[Pasted image 20251010161234.png]]

Hash 函数

默认情况下,HashMap 使用加密功能强大的 Hash 函数,可以抵抗拒绝服务攻击

  • 不是最快的Hash算法
  • 具有更好的安全性
    可以指定不同的 hasher 来切换到另一个函数
  • hasher 是实现 BuildHasher trait 的类型
http://www.dtcms.com/a/466091.html

相关文章:

  • 三合一网站建设公司杭州科技公司排名
  • 温州建设监理协会网站录入客户信息的软件
  • 38.Shell脚本编程2
  • ETLCloud-重塑制造业数据处理新范式
  • 【JavaSE】JVM
  • 部分网站dns解析失败wordpress 图片预加载
  • django 网站开发案例公众号微信
  • 数据库进阶实战:从性能优化到分布式架构的核心突破
  • MySQLEXPLAIN命令详解从执行计划看SQL性能优化
  • leetcode 506 斐波那契数
  • Linux 命令:mount
  • JavaWeb——Servlet生命周期
  • JavaWeb——(web.xml)中的(url-pattern)
  • 企业网站建设合作协议范文天津城市建设大学网站
  • 新专业加速落地!设备采购先行,工业视觉人才培养破局。
  • FastAPI 入门:从环境搭建到实战开发的完整指南
  • Redis的String详解
  • MySQL事务隔离级别详解从读未提交到可串行化
  • 网站域名注册空间app外包
  • 赣州网站推广公司微网站建设是什么
  • 图扑 HT 架构下 AR 应用开发与行业解决方案实现
  • 测试实战心得
  • 网页网站设计价格为什么我做的视频网站播放不了
  • 前端框架深度解析:Vue.js 3 从 Composition API 到生态升级,解锁企业级开发新能力
  • DataX适合全量同步和简单的增量场景
  • 实体门店怎么使用小程序?实体店如何做小程序店铺?
  • 服装公司网站建设方案渭南做网站博创互联
  • 基于GPS/PTP/gPTP的自动驾驶数据同步授时方案
  • 福田网站建设龙岗网站建设龙岗网站建设龙岗网站建设中关村手机之家官网
  • solr负查询失效