Rust语言中的String和HashMap使用示例详解

 更新时间:2022年10月18日 15:47:50   作者:SaraiNoQ  
这篇文章主要介绍了Rust语言中的String和HashMap使用示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

String

字符串是比很多开发者所理解的更为复杂的数据结构。加上 UTF-8 的不定长编码等原因,Rust 中的字符串并不如其它语言中那么好理解。

Rust 的核心语言中只有一种字符串类型:str。字符串 slice,它通常以被借用的形式出现:&str 是一些储存在别处的 UTF-8 编码字符串数据的引用。而 String 的类型是由标准库提供的,而没有写进核心语言部分,它是可增长的、可变的、有所有权的、UTF-8 编码的字符串类型。

💡 Rust 标准库中还包含一系列其他字符串类型,比如 OsStringOsStrCStringCStr。相关库 crate 还会提供更多储存字符串数据的数据类型。这些字符串类型能够以不同的编码,或者内存表现形式上以不同的形式,来存储文本内容。

新建字符串

  • String::new()函数
  • to_string()方法
let data = "initial contents";
let s = data.to_string();
// 或者
let s = String::from(data);
// 该方法也可直接用于字符串字面量:
let s = "initial contents".to_string();

更新字符串

String 的大小可以增加,其内容也可以改变。另外,还可以使用 + 运算符或 format! 宏来拼接 String 值。

  • push_str()
  • push()
let mut s = String::from("foo");
let t = String::from("bar");
s.push_str(&t);
// push 方法被定义为获取一个单独的字符作为参数,并附加到 String 中
let mut l = String::from("lo");
l.push('l');

💡 pub fn push_str(&mut self, string: &str) 方法不会获得字符串的所有权。另外值得一提的是,t&String 类型,而 push_str 方法需要的是 &str 类型的参数。为什么这段代码能够正常编译呢?这里就涉及到了 解引用强制转换(deref coercion),我们将在后面的文章中介绍它。

使用 + 运算符或 format! 宏拼接字符串

let s1 = String::from("Hello, ");
let s2 = String::from("world!");
let s3 = s1 + &s2;

你可以把他理解成 C++ 的运算符重载。在 Rust 中, + 的实现可能是 fn add(self, s: &str) -> String 这样一个方法。

💡 s1 的所有权将被移动到 add 调用中

如果想要级联多个字符串,使用 + 就变得麻烦了。这时候可以使用 format! 宏:

let s1 = String::from("hello");
let s2 = String::from("the");
let s3 = String::from("world");
let s = format!("{}-{}-{}", s1, s2, s3);

索引字符串

在其他语言中,通过索引来引用字符串中的某个单独字符是很常见的操作。但在 Rust 中,你可能会遇到问题:

这主要是因为:

  • UTF-8 是不定长编码,而 String 的实现是基于 Vec<u8> 的封装:数组中每一个元素都是一个字节,但 UTF-8 中每一个汉字(或字符)都可能由一到四个字节组成
  • 索引操作预期总是需要常数时间 (O(1))。但是对于 String 不可能保证这样的性能,因为 Rust 必须从开头到索引位置遍历来确定有多少有效的字符。

字符串 slice

如果你真的希望使用索引创建字符串 slice 时,Rust 会要求你明确字符串范围。这时你需要一个字符串 slice,使用 [] 和一个 range 来创建含特定字节的字符串 slice:

fn main() {
    let s1 = String::from("你好,");
    println!("{}", &s1[0..3]);  // 你
}

如果获取 &s1[0..1] ,Rust 在运行时会 panic。因此,你应该谨慎地使用这个操作,因为这么做可能会使你的程序崩溃。

遍历字符串

可以使用 chars() 方法获取该字符串的字母数组。

fn main() {
    let s1 = String::from("你好,");
    let s2 = String::from("世界!");
    let s3 = s1 + &s2; 
    for char in s3.chars() {
        println!("{}", char);
    }
}

HashMap

另外一个常用集合类型是 哈希 map(hash map)。HashMap<K, V> 类型储存了一个键类型 K 对应一个值类型 V 的映射。它通过一个 哈希函数(hashing function)来实现映射,决定如何将键和值放入内存中。

哈希 map 适用于需要任何类型作为键来寻找数据的情况,而不是像 vector 那样通过索引。

新建 HashMap

使用new 创建一个空的 HashMap,并使用 insert 增加元素:

use std::collections::HashMap;
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);

💡 必须首先 use 标准库中集合部分的 HashMap。在这上面介绍的三个常用集合中,HashMap 是最不常用的,所以并没有被 prelude 自动引用。标准库中对 HashMap 的支持也相对较少,例如,并没有内建的构建宏。

💡 像 vector 一样,哈希 map 将它们的数据储存在堆上;哈希 map 是同质的:所有的键必须是相同类型,值也必须都是相同类型。

另一个构建哈希 map 的方法是使用一个元组的 vector 的 collect 方法:

use std::collections::HashMap;
let teams  = vec![String::from("Blue"), String::from("Yellow")];
let initial_scores = vec![10, 50];
let scores: HashMap<_, _> = teams.iter().zip(initial_scores.iter()).collect();

HashMap 和 ownership

  • 对于像 i32 这样的实现了 Copy trait 的类型,其值可以拷贝进哈希 map。
  • 对于像 String 这样拥有所有权的值,其值将被移动而哈希 map 会成为这些值的所有者。
use std::collections::HashMap;
let field_name = String::from("Favorite color");
let field_value = String::from("Blue");
let mut map = HashMap::new();
map.insert(field_name, field_value);
// 此时 field_name 和 field_value 被移动到了 map 中

💡 如果将值的引用插入哈希 map,这些值本身将不会被移动进哈希 map。但是这些引用指向的值必须至少在哈希 map 有效时也是有效的。此时就涉及到生命周期的内容。

访问 HashMap 中的值

可以通过 get 方法并提供对应的键来从哈希 map 中获取值:

use std::collections::HashMap;
let mut 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);

get 返回 Option<V>,所以结果被装进 Some;如果某个键在哈希 map 中没有对应的值,get 会返回 None。当获取到结果后,就需要使用到 match 进行匹配。

更新 HashMap

在更新前,我们需要考虑以下几种情况:

  • 已有 key-value,直接覆盖
  • 只在没有 key-value插入
  • 利用已有 key-value更新

直接覆盖

insert() 方法:

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

新插入

利用 entry() 函数返回的枚举值,调用 or_insert() 方法进行处理:

use std::collections::HashMap;
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 方法在键对应的值存在时就返回这个值的可变引用,如果不存在则将参数作为新值插入并返回新值的可变引用。

更新旧值

or_insert 方法事实上会返回这个键的值的一个可变引用(&mut V):

// 统计字符串中某个单词的出现次数
use std::collections::HashMap;
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); // 之前不存在对应关系就初始化并置计数器为0
    *count += 1; // 每次计数器加一
}
println!("{:?}", map);

💡 这里我们将这个可变引用储存在 count 变量中,所以为了赋值必须首先使用星号( * )解引用 count。这个可变引用在 for 循环的结尾离开作用域,这样所有这些改变都是安全的并符合借用规则。

总结

掌握了 Rust 中最常用的三种集合类型,现在你已经可以开始进行一些包含复杂逻辑的编程了!在此过程中你可能会遇到很多错误。因此接下来我将介绍错误处理与模式匹配,并开始介绍一些测试工具和自动化测试的内容,更多关于Rust String HashMap使用的资料请关注脚本之家其它相关文章!

相关文章

  • 利用Rust编写一个简单的字符串时钟

    利用Rust编写一个简单的字符串时钟

    这篇文章主要为大家详细介绍了一个用rust写的一个简单的练手的demo,一个字符串时钟,在终端用字符串方式显示当前时间,感兴趣的小伙伴可以了解一下
    2022-12-12
  • rust的package,crate,module示例解析

    rust的package,crate,module示例解析

    rust提供了非常优秀的包管理器cargo,我们可以使用crate,module,package来组织代码,这篇文章主要介绍了rust的package,crate,module相关知识,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-04-04
  • Rust文件 launch.json作用大全

    Rust文件 launch.json作用大全

    launch.json 是 Visual Studio Code(VSCode)中的一个配置文件,主要用于配置调试器,本文给大家介绍Rust文件 launch.json 有什么用,感兴趣的朋友跟随小编一起看看吧
    2024-05-05
  • 使用Rust制作康威生命游戏的实现代码

    使用Rust制作康威生命游戏的实现代码

    这篇文章主要介绍了使用Rust制作康威生命游戏,初始rust项目,使用wasm的项目模板,结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-09-09
  • Rust如何使用Sauron实现Web界面交互

    Rust如何使用Sauron实现Web界面交互

    Sauron 是一个多功能的 Web 框架和库,用于构建客户端和/或服务器端 Web 应用程序,重点关注人体工程学、简单性和优雅性,这篇文章主要介绍了Rust使用Sauron实现Web界面交互,需要的朋友可以参考下
    2024-03-03
  • 一文掌握Rust编程中的生命周期

    一文掌握Rust编程中的生命周期

    在Rust语言中, 每一个引用都有其生命周期, 通俗讲就是每个引用在程序执行的过程中都有其自身的作用域, 一旦离开其作用域, 其生命周期也宣告结束, 值不再有效,这篇文章主要介绍了Rust编程中的生命周期,需要的朋友可以参考下
    2023-11-11
  • Rust处理命令行参数

    Rust处理命令行参数

    在Rust中,命令行参数是程序从命令行接收的输入,它们为程序提供了运行时配置和数据的灵活性,本文就来介绍一下Rust处理命令行参数,具有一定的参考价值,感兴趣的可以了解一下
    2024-03-03
  • Rust自定义安装路径的详细图文教程

    Rust自定义安装路径的详细图文教程

    工欲善其事必先利其器,无论是对小白还是大神来说,想要学习 Rust 第一步那必须是 Rust 的环境配置,下面这篇文章主要给大家介绍了关于Rust自定义安装路径的详细图文教程,需要的朋友可以参考下
    2023-03-03
  • Rust 搭建一个小程序运行环境的方法详解

    Rust 搭建一个小程序运行环境的方法详解

    rust是一门比较新的编程语言,2015年5月15日,Rust编程语言核心团队正式宣布发布Rust 1.0版本,本文给大家介绍Rust 搭建一个小程序运行环境,以iOS 为例介绍开发环境的准备,感兴趣的朋友跟随小编一起看看吧
    2022-05-05
  • Rust使用libloader调用动态链接库

    Rust使用libloader调用动态链接库

    这篇文章主要为大家介绍了Rust使用libloader调用动态链接库示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-09-09

最新评论