【Rust指南】结合String深入理解Rust语言的Slice(切片)类型

简介: 【Rust指南】结合String深入理解Rust语言的Slice(切片)类型

Rust Slice(切片)类型


切片(Slice)是对数据值的部分引用,是一种不持有所有权的数据类型。


切片这个名字往往出现在生物课上,我们做样本玻片的时候要从生物体上获取切片,以供在显微镜上观察。在 Rust 中,切片的意思大致也是这样,只不过它属于数据的取材引用。


一、编写处理字符串的函数


这里提供两种实现方法:一是通过以往的知识直接用String类型来解答,另一种是学习过本文的字符串切片类型后再去解答。


1、目标函数特点


接收字符串作为参数

返回它在这个字符串里找到的第一个单词

如果没有找到空格,将字符串全部返回


2、使用String粗略实现


既然是找到第一个单词,不妨找到第一个空格所在的索引,后续从头开始打印到索引位置即可:


fn main() {
    println!("切片的学习");
    let mut s =String::from("hello world");
    let index=first_world(&s);
    s.clear();//这里清空s字符串,但是仍然可以得到第一个空格的索引
    println!("第一个空格出现的索引为:{}",index);
}
fn first_world(str:&String)->usize{
    let bytes=str.as_bytes();
    for(i,&item) in bytes.iter().enumerate(){
        if item==b' '{
        return i;
        }
    }
    str.len()
}


运行结果:



这里创建String类型的s并赋值"hello world",第一个空格的索引应为5,接下来用first_world函数来实现。


 函数形参的类型是String的引用,返回值是usize,str.as_bytes()含义为将str字符串转换为一个字节数组bytes,然后我们开始用for循环对字节数组遍历:

 for(i,&item) in bytes.iter().enumerate() 中 (i,&iten) 是一个元组,i是元组的索引,即每个i对应着一个item,要注意它是一个引用,我们加上&之后解引用就成了一个u8类型的值了;然后bytes.iter()是该字节数组的一个迭代器,会依次返回字节数组里的元素;enumerate()方法会将迭代器遍历的元素包装成一个个的小元组,而刚开始for循环里的元组实际上是一个模式匹配,用来解构返回的小元组从而得到字节数组中每个索引和每个索引对应的元素值。

 将每个元素值和b' '(b’A’是字节的数据类型' '内用来放置特定的字节,这里我直接填上一个空格)比较,如果相等就返回索引,也就是第一个空格的索引,如果遍历到最后都没有空格,那就返回整个字符串的长度。


在主函数中调用first_world方法,注意里面传入的是字符串的引用,而且必须加上&,这点是不同于C++的。并用index接收该函数的返回值。虽然程序写完了而且可以得到正确结果,但其实是有bug存在的:此时的字符串s和索引并没有关联,如果我把字符串清空,此时仍然可以输出索引位置,这很明显是不合理的,因此就要使用接下来分享的字符串切片来解决这个bug。


3、使用字符串切片完整实现


fn main() {
    println!("切片的学习");
    let str=String::from("hello rust");
    let new_str=first_world_slice(&str[..]);
    //str.clear();不可将变量同时借用为可变和不可变的状态
    println!("字符串中第一个单词是:{}",new_str);
}
fn first_world_slice(s:&str) ->&str{
    let bytes=s.as_bytes();
    for(i,&it) in bytes.iter().enumerate(){
        if it==b' '{
            return &s[..i]
        }
    }
    &s[..]
}


运行效果:



这里主函数调用函数时传参传入的是&str[. .],即为字符串str转换为完整的字符串切片类型;函数内的返回部分也做了修改,存在空格的话就直接返回其索引前的字符串切片,如果没有空格则返回整个字符串。如果这时候执行str.clear(),编译器就会提示错误,原因就是违反了变量租借的原则,由此可见Rust语言可以使一些API变得简单通用且可以在编译阶段就能阻止错误的发生。


 细心观察的朋友可以看到函数的返回值被我改成了字符串切片类型,这样就可以传入字符串或者字符串切片两种类型的参数了:


传入的参数如果使字符串切片就直接写入

如果传入的是字符串类型,那么就创建一个完整的字符串切片即可

定义函数时使用字符串切片来代替字符串引用会使我们的API更加通用,且不会损失功能


二、字符串切片及其与字符串的区别


最简单、最常用的数据切片类型是字符串切片(String Slice)


例如:


fn main() {
    let s = String::from("broadcast");
    let part1 = &s[0..5];
    let part2 = &s[5..9];
    println!("{}={}+{}", s, part1, part2);
}
//运行结果:broadcast=broad+cast


Rust 中的字符串类型实质上记录了字符在内存中的起始位置和其长度

part1在内存中的起始位置和字符串s一致

part2在内存中的起始位置指向字符c

s的长度为9,part1长度为5,part2长度为4

&s[x..y]就是字符串切片类型的格式,取值上是前开后闭的:[ x , y ) [x,y)[x,y)

. .y 等价于 0. .y

x. . 等价于位置 x 到数据结束

. . 等价于位置 0 到结束

注意事项

字符串切片的范围索引必须发生在有效的utf-8字符边界内

这是编码问题,后续文章会详细说明

如果尝试从一个多字节的字符串切片中创建字符串切片,程序会报错并退出

这是因为切片类型是没有所有权的

实际上,到目前为止你一定疑惑为什么每一次使用字符串都要这样写String::from("runoob") ,直接写 "runoob" 不行吗?


事已至此我们必须分辨这两者概念的区别了。在 Rust 中有两种常用的字符串类型:str 和 String :


str 是 Rust 核心语言类型,就是本章一直在讲的字符串切片(String Slice),常常以引用的形式出现(&str)。

凡是用双引号包括的字符串常量整体的类型性质都是 &str :


let s = "hello";

1

这里的 s 就是一个 &str 类型的变量。


String 和 str 除了同样拥有一个字符开始位置属性和一个字符串长度属性以外还有一个容量(capacity)属性。


String 和 str 都支持切片,切片的结果是 &str 类型的数据。


注意:切片结果必须是引用类型,但开发者必须自己明示这一点


三、非字符串切片的使用


除了字符串以外,其他一些线性数据结构也支持切片操作,例如数组:


fn main() {
    let arr = [1, 3, 5, 7, 9];
    let part = &arr[1..3];
    for i in part.iter() {
        println!("{}", i);
    }
}
//运行结果为:3 5


这里的part是一个数组的切片,起始元素为arr数组的索引1的值,最后一个元素应为arr数组索引2的值

然后利用迭代器依次返回数组切片的元素值

相关文章
|
1月前
|
Rust 安全 Java
探索Rust语言的并发编程模型
探索Rust语言的并发编程模型
|
1月前
|
Rust 安全 区块链
探索Rust语言:系统编程的新选择
【10月更文挑战第27天】Rust语言以其安全性、性能和并发性在系统编程领域受到广泛关注。本文介绍了Rust的核心特性,如内存安全、高性能和强大的并发模型,以及开发技巧和实用工具,展示了Rust如何改变系统编程的面貌,并展望了其在WebAssembly、区块链和嵌入式系统等领域的未来应用。
|
1月前
|
Rust 安全 Java
编程语言新宠:Rust语言的特性、优势与实战入门
【10月更文挑战第27天】Rust语言以其独特的特性和优势在编程领域迅速崛起。本文介绍Rust的核心特性,如所有权系统和强大的并发处理能力,以及其性能和安全性优势。通过实战示例,如“Hello, World!”和线程编程,帮助读者快速入门Rust。
73 1
|
1月前
|
Rust 安全 编译器
编程语言新宠:Rust语言的特性、优势与实战入门
【10月更文挑战第26天】Rust语言诞生于2006年,由Mozilla公司的Graydon Hoare发起。作为一门系统编程语言,Rust专注于安全和高性能。通过所有权系统和生命周期管理,Rust在编译期就能消除内存泄漏等问题,适用于操作系统、嵌入式系统等高可靠性场景。
97 2
|
1月前
|
Rust 安全
深入理解Rust语言的所有权系统
深入理解Rust语言的所有权系统
36 0
|
1月前
|
Rust 安全 前端开发
探索Rust语言的异步编程模型
探索Rust语言的异步编程模型
|
2月前
|
数据可视化 Java
让星星月亮告诉你,通过反射创建类的实例对象,并通过Unsafe theUnsafe来修改实例对象的私有的String类型的成员属性的值
本文介绍了如何使用 Unsafe 类通过反射机制修改对象的私有属性值。主要包括: 1. 获取 Unsafe 的 theUnsafe 属性:通过反射获取 Unsafe类的私有静态属性theUnsafe,并放开其访问权限,以便后续操作 2. 利用反射创建 User 类的实例对象:通过反射创建User类的实例对象,并定义预期值 3. 利用反射获取实例对象的name属性并修改:通过反射获取 User类实例对象的私有属性name,使用 Unsafe`的compareAndSwapObject方法直接在内存地址上修改属性值 核心代码展示了详细的步骤和逻辑,确保了对私有属性的修改不受 JVM 访问权限的限制
64 4
|
2月前
|
存储 分布式计算 NoSQL
大数据-40 Redis 类型集合 string list set sorted hash 指令列表 执行结果 附截图
大数据-40 Redis 类型集合 string list set sorted hash 指令列表 执行结果 附截图
29 3
|
1月前
|
Rust 安全 云计算
Rust语言入门:安全性与并发性的完美结合
【10月更文挑战第25天】Rust 是一种系统级编程语言,以其独特的安全性和并发性保障而著称。它提供了与 C 和 C++ 相当的性能,同时确保内存安全,避免了常见的安全问题。Rust 的所有权系统通过编译时检查保证内存安全,其零成本抽象设计使得抽象不会带来额外的性能开销。Rust 还提供了强大的并发编程工具,如线程、消息传递和原子操作,确保了数据竞争的编译时检测。这些特性使 Rust 成为编写高效、安全并发代码的理想选择。
32 0
|
2月前
|
Rust 安全 网络安全
在 Rust 语言中,寻找企业上网行为管理软件的突破
在数字化企业环境中,上网行为管理软件对于保障网络安全和提升工作效率至关重要。Rust 语言凭借其安全性、高性能和并发性,为开发此类软件提供了新机遇。本文通过几个 Rust 代码示例,展示了如何实现网址检查、访问频率统计及访问控制等功能,旨在探索 Rust 在企业上网行为管理中的应用潜力。
43 0