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的Rc<T>:实现多所有权的智能指针方式

    解读Rust的Rc<T>:实现多所有权的智能指针方式

    Rc<T> 是 Rust 中用于多所有权的引用计数类型,通过增加引用计数来管理共享数据,只有当最后一个引用离开作用域时,数据才会被释放,Rc<T> 适用于单线程环境,并且只允许不可变共享数据;需要可变共享时应考虑使用 RefCell<T> 或其他解决方案
    2025-02-02
  • rust的vector和hashmap详解

    rust的vector和hashmap详解

    这篇文章主要介绍了rust的vector和hashmap,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-03-03
  • Rust并发编程之使用消息传递进行线程间数据共享方式

    Rust并发编程之使用消息传递进行线程间数据共享方式

    文章介绍了Rust中的通道(channel)概念,包括通道的基本概念、创建并使用通道、通道与所有权、发送多个消息以及多发送端,通道提供了一种线程间安全的通信机制,通过所有权规则确保数据安全,并且支持多生产者单消费者架构
    2025-02-02
  • Rust语言之trait中的个方法可以重写吗

    Rust语言之trait中的个方法可以重写吗

    在Rust中,trait定义了一组方法,这些方法可以被一个或多个类型实现,当你为某个类型实现一个trait时,你可以为该trait中的每个方法提供自己的具体实现,本文将给大家介绍一下trait中的个方法是否可以重写,需要的朋友可以参考下
    2023-10-10
  • Rust 实现 async/await的详细代码

    Rust 实现 async/await的详细代码

    异步编程在 Rust 中的地位非常高,很多 crate 尤其是多IO操作的都使用了 async/await,这篇文章主要介绍了Rust 如何实现 async/await,需要的朋友可以参考下
    2022-09-09
  • Rust语言的新手了解和学习入门启蒙教程

    Rust语言的新手了解和学习入门启蒙教程

    这篇文章主要介绍了rust的特点、安装、项目结构、IDE环境配置、代码运行,讲解了如何安装Rust编译器,创建和运行第一个Rust程序,并对Rust语言的特点和优势作了说明,包括内存安全、高效性能、并发性、社区支持和统一包管理等,是新手了解和学习Rust语言的启蒙教程
    2024-12-12
  • rust中间件actix_web在项目中的使用实战

    rust中间件actix_web在项目中的使用实战

    这篇文章主要介绍了rust中间件在项目中的使用实战,包括自定义中间件,日志中间件,Default headers,用户会话,错误处理的用法实例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2024-01-01
  • Rust中的关联类型总结

    Rust中的关联类型总结

    关联类型是定义通用trait的一种机制。它允许在trait中定义一个或多个占位符类型,这些类型将在trait的实现中具体化。文中有详细示例代码供参考,需要的朋友可以阅读一下
    2023-05-05
  • 详解Rust中的所有权机制

    详解Rust中的所有权机制

    Rust 语言提供了跟其他系统编程语言相同的方式来控制你使用的内存,但拥有数据所有者在离开作用域后自动清除其数据的功能意味着你无须额外编写和调试相关的控制代码,这篇文章主要介绍了Rust中的所有权机制,需要的朋友可以参考下
    2022-10-10
  • rust中async/await的使用示例详解

    rust中async/await的使用示例详解

    在Rust中,async/await用于编写异步代码,使得异步操作更易于理解和编写,通过使用await,在async函数或代码块中等待Future完成,而不会阻塞线程,允许同时执行其他Future,这种机制简化了异步编程的复杂性,使代码更加直观
    2024-10-10

最新评论