【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的值

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

相关文章
|
8天前
|
数据可视化 Java
让星星月亮告诉你,通过反射创建类的实例对象,并通过Unsafe theUnsafe来修改实例对象的私有的String类型的成员属性的值
本文介绍了如何使用 Unsafe 类通过反射机制修改对象的私有属性值。主要包括: 1. 获取 Unsafe 的 theUnsafe 属性:通过反射获取 Unsafe类的私有静态属性theUnsafe,并放开其访问权限,以便后续操作 2. 利用反射创建 User 类的实例对象:通过反射创建User类的实例对象,并定义预期值 3. 利用反射获取实例对象的name属性并修改:通过反射获取 User类实例对象的私有属性name,使用 Unsafe`的compareAndSwapObject方法直接在内存地址上修改属性值 核心代码展示了详细的步骤和逻辑,确保了对私有属性的修改不受 JVM 访问权限的限制
25 4
|
14天前
|
存储 分布式计算 NoSQL
大数据-40 Redis 类型集合 string list set sorted hash 指令列表 执行结果 附截图
大数据-40 Redis 类型集合 string list set sorted hash 指令列表 执行结果 附截图
21 3
|
9天前
|
Rust 安全 网络安全
在 Rust 语言中,寻找企业上网行为管理软件的突破
在数字化企业环境中,上网行为管理软件对于保障网络安全和提升工作效率至关重要。Rust 语言凭借其安全性、高性能和并发性,为开发此类软件提供了新机遇。本文通过几个 Rust 代码示例,展示了如何实现网址检查、访问频率统计及访问控制等功能,旨在探索 Rust 在企业上网行为管理中的应用潜力。
16 0
|
28天前
|
设计模式 Rust 安全
30天拿下Rust之高级类型
30天拿下Rust之高级类型
22 0
|
1月前
|
Rust 安全 索引
30天拿下Rust之切片
在Rust中,切片是一种非常重要的引用类型。它允许你安全地引用一段连续内存中的数据,而不需要拥有这些数据的所有权。切片不包含分配的内存空间,它仅仅是一个指向数据开始位置和长度的数据结构。切片是对数组的一个连续引用,它提供了一种方便、高效的方式来操作数组的一部分。切片本身并不拥有数据,它只是原始数组的一个视图,因此创建切片通常是一个低开销的操作。
18 1
|
2月前
|
Rust 安全 程序员
Rust 语言的防错机制太惊人了!安全编码从此不再是难题,快来一探究竟!
【8月更文挑战第31天】《安全编码原则:Rust 语言中的防错机制》探讨了代码安全的重要性,并详细介绍了Rust语言如何通过内存安全模型、所有权与借用规则等特性,在编译阶段检测并阻止潜在错误,如缓冲区溢出和悬空指针。文章还讨论了类型安全、边界检查等其他安全特性,并提出了遵循不可变引用、避免裸指针及充分测试等实用编码原则,以进一步提升代码质量和安全性。随着Rust在软件开发中的应用日益广泛,掌握其安全编码原则变得尤为重要。
50 0
|
2月前
|
Rust 安全 调度
从零构建梦想操作系统:用Rust语言亲手打造专属内核,你也可以成为系统开发者!
【8月更文挑战第31天】开发操作系统内核虽具挑战,却也充满乐趣。本文将指导你从零开始,使用Rust语言构建一个简单的操作系统内核。首先安装Rust环境和交叉编译工具链,然后创建新项目`my_kernel`。通过修改`Cargo.toml`和编写启动函数,结合串口输出和`multiboot2`库,最终使用QEMU运行内核。此教程旨在帮助你理解Rust在系统开发中的应用,激发深入探索的兴趣。
66 1
|
2月前
|
Rust 安全 算法
揭秘Rust语言如何重塑区块链安全:打造坚不可摧的分布式账本新篇章!
【8月更文挑战第31天】自比特币诞生以来,区块链技术凭借其去中心化和不可篡改的特点备受关注。为了应对安全性挑战,Rust 语言凭借其内存安全特性逐渐成为区块链开发的优选。本文探讨了 Rust 如何助力区块链实现更安全的分布式账本。通过示例展示了 Rust 在避免内存泄漏、空指针引用及数据竞争等方面的优势,预示着 Rust 在高性能、高安全性需求的区块链应用中拥有广阔前景。
70 1
|
1月前
|
Rust Linux Go
Rust/Go语言学习
Rust/Go语言学习
|
2月前
|
开发者 vr&ar 机器学习/深度学习
Xamarin 开发者的未来趋势展望:掌握跨平台开发新机遇,引领移动应用创新潮流与技术变革方向
【8月更文挑战第31天】Xamarin 作为领先的跨平台开发框架,通过 C# 和 .NET 框架实现一次编写、多平台运行,简化了 iOS、Android 和 Windows 应用的开发流程。未来几年,Xamarin 开发者将面临跨平台开发普及、云集成、机器学习、AR/VR、性能优化及安全性等关键趋势。通过学习新技术并积极采用新工具,开发者能够提升应用质量和用户体验,如利用 Azure AD B2C 实现身份认证,从而在竞争激烈的市场中脱颖而出。
55 0