Rust开发——切片(slice)类型

1、什么是切片

在 Rust 中,切片(slice)是一种基本类型和序列类型。在 Rust 官方文档中,切片被定义为“对连续序列的动态大小视图”。

但在rust的Github 源码中切片被定义如下:

切片是对一块内存的视图,表示为指针和长度。

其实这个定义更有帮助。从这里的定义可以知道,切片是一个“宽指针”(fat pointer)。所以基本上,当创建一个数组的切片时,切片包含以下内容:

  • 指向数组中切片起始元素地址的指针
  • 描述切片长度的值

2、切片示例

在 Rust 中,切片可以是对支持的数组的视图,也可以是对其他序列(例如向量或字符串)的视图。如果切片是对字符串的视图,它被称为字符串切片或字符串字面量,并且通常以其借用形式 &str 出现。

以下是一个示例数组和该数组生成的两个切片:
在这里插入图片描述
左边和右边展示了对中间显示的数组提供视图的两个切片。数组和切片的定义如下:

let array: [i32; 10] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
let slice1 = &array[5..10];
let slice2 = &array[3..7];

从上图可以看到,在 slice1 中,切片的指针指向数组的索引 5。slice1 的长度为 5。这意味着切片将包含数组中的 5 个元素。下面是切片相关的索引和值。切片本身的索引从 0 到 4。

右侧是 slice2。该切片的指针指向元素 3,且切片的长度为 4。

3、切片常规操作

定义一个数组,然后对数组进行切片操作:

let array: [i32; 7] = [0, 1, 2, 3, 4, 5, 6];

let slice = &array[..]; // [ 0, 1, 2, 3, 4, 5, 6 ]
let slice = &array[0..3]; // [ 0, 1, 2 ]
let slice = &array[..3]; // [ 0, 1, 2 ]
let slice = &array[2..4]; // [ 2, 3 ]
let slice = &array[2..]; // [ 2, 3, 4, 5, 6 ]

上面定义了不可变数组以及创建数组切片的几种方法。在切片定义后的注释中展示了 dbg!(slice); 的输出结果。

之后再创建一个可变的切片:

let mut array: [i32; 7] = [0, 1, 2, 3, 4, 5, 6];
let array_slice = &mut array[0..5]; // [ 0, 1, 2, 3, 4 ]

在这里插入图片描述
检查切片的长度并迭代索引/值:

slice.len(); // 5

for (index, item) in slice.iter().enumerate() {
    println!("index: {:?} element {:?}", index, item);
}
/*
index: 0 element 0
index: 1 element 1
index: 2 element 2
index: 3 element 3
index: 4 element 4
*/

从切片中检索一个值:

slice[1]; // 1

切片的长度在编译时并不总是已知的。如果访问超出边界的索引值,编译器将不会保存:

slice[100];

会报如下错误:

thread ‘main’ panicked at ‘index out of bounds: the len is 5 but the index is 100’

为了安全地从切片中获取值,可以使用 get() 方法:

slice.get(2); // Some(2)
slice.get(100); // None

在切片中查找值:

slice.iter().position(|v| v == &120); // None
slice.iter().position(|v| v == &4); // Some(4)

改变切片中元素的值:

slice[0] = 100;
dbg!(slice); // [100, 1, 2, 3, 4]
dbg!(array); // [100, 1, 2, 3, 4, 5, 6]

4.对不同类型的切片进行操作

可以从数组、向量和字符串中获取切片:

let array: [i32; 4] = [0, 1, 2, 3];
let array_slice = &array[..2]; // [0, 1]
let vector = vec![1, 2, 3, 4];
let vector_slice = &vector[0..2]; // [1, 2]
let string = String::from("string slice");
let string_slice = &string[0..6]; // "string"
println!("{:?} {:?} {:?}", array_slice, vector_slice, string_slice);
// [0, 1] [1, 2] "string"

之前定义的数组和向量包含 i32 类型,之后可以创建一个同时适用于 vector_slicearray_slice 的函数:

fn return_second(n: &[i32]) {
    println!("{}", n[1]);
}
return_second(array_slice); // 1
return_second(vector_slice); // 2

字符串切片是一个 &str,因此不能将其传递给 return_second 函数。事实上,字符串切片有点特殊。在 Rust 中,所有的字符串都是 UTF-8 编码的,因此字符的大小可以不同。iter() 方法不能用在字符串切片上,相反,需要使用 chars() 方法。要从切片中取第 n 个字符,则需要使用索引 n。

let string = String::from("Rust is 😍");
let string_slice = &string[..];

fn return_second_char(n: &str) {
    println!("{:?}", n.chars().nth(1));
}

return_second_char(string_slice); // Some('u')

for c in string_slice.chars() {
    println!("{}", c)
}
/*
R
u
s
t

i
s

😍
*/
for (i, c) in string_slice.chars().enumerate() {
    println!("{} {}", i, c)
}
/*
0 R
1 u
2 s
3 t
4
5 i
6 s
7
8 😍
*/

5.指针

Rust中的宽指针(fat pointers)与窄指针(thin pointers)是指针类型的两种概念。

  • 窄指针(Thin Pointers):指针仅包含目标内存地址信息,不包含其他附加信息。比如,裸指针 *const T*mut T 就是窄指针,它们只存储指向某个类型 T 的内存地址。

  • 宽指针(Fat Pointers):指针除了存储目标内存地址外,还包含其他信息,例如动态数组的长度。切片 &[T] 或者动态 trait 对象 &dyn Trait 就是宽指针的例子,它们除了指向内存的地址外,还存储着长度等其他信息。

宽指针包含更多的信息,但也会带来一些额外的存储开销。窄指针更加轻量,但缺乏一些额外的信息。在Rust中,切片是一种宽指针,因为它包含指向数据的指针和数据长度。

use std::mem;
let array: [i32; 500] = [0; 500];
let slice = &array[..];
let array_pointer = &array;
let slice_pointer = &slice;
let start_of_array_slice = &array[0];
println!("--------------------------------------------");
println!("array_pointer address: {:p}", array_pointer);
println!("slice_pointer address: {:p}", slice_pointer);
println!("start_of_array_slice address: {:p}", start_of_array_slice);
println!("slice occupies {} bytes", mem::size_of_val(&slice));
println!(
    "array_pointer occupies {} bytes",
    mem::size_of_val(&array_pointer)
);
println!("array occupies {} bytes", mem::size_of_val(&array));
println!("--------------------------------------------");
--------------------------------------------
array_pointer address: 0x9def68
slice_pointer address: 0x9df738
start_of_array_slice address: 0x9def68
slice occupies 16 bytes
array_pointer occupies 8 bytes
array occupies 2000 bytes
--------------------------------------------

数组的总大小为 2000 字节。整个数组的切片(宽指针)占据 16 字节。如果获取数组的指针,得到的是一个占据 8 字节的窄指针。数组指针和切片起始地址的内存地址是相同的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/165970.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何隐藏Selenium特征实现自动化网页采集

Selenium是一个流行的自动化网页测试工具,可以通过模拟用户在Chrome浏览器中的操作来完成网站的测试。然而,有些网站会检测浏览器是否由Selenium驱动,如果是,就会返回错误的结果或拒绝访问。为了避免这种情况,我们需要…

多线程编程

1 线程的使用 1.1 为什么要使用多线程 在编写代码时,是否会遇到以下的场景会感觉到难以下手? 要做 2 件事,一件需要阻塞等待,另一件需要实时进行。例如播放器:一边在屏幕上播放视频,一边在等待用户的按…

中间件安全:Apache 目录穿透.(CVE-2021-41773)

中间件安全:Apache 目录穿透.(CVE-2021-41773) Apache 的 2.4.49、2.4.50 版本 对路径规范化所做的更改中存在一个路径穿越漏洞,攻击者可利用该漏洞读取到Web目录外的其他文件,如系统配置文件、网站源码等&#xff0c…

Polygon zkEVM协议治理、升级及其流程

1. 引言 随着Polygon社区开发者和内部团队的测试深入,当前版本的Polygon zkEVM不可避免地需更新和某些升级。 为激励开发者对Polygon zkEVM做battle-test,已启动了bug-bounty: Rewards by Threat Level 由于zk-Rollup生态系统还处于萌芽阶…

算法设计与分析复习--贪心(二)

文章目录 上一篇哈夫曼编码单源最短路最小生成树Kruskal算法Prim算法 多机调度问题下一篇 上一篇 算法设计与分析复习–贪心&#xff08;一&#xff09; 哈夫曼编码 产生这种前缀码的方式称为哈夫曼树 哈夫曼树相关习题AcWing 148. 合并果子 #include <iostream> #inc…

LDO线性稳压器要不要并联二极管?

昨天介绍过了LDO是什么东西&#xff0c;那么对于它的应用场景是怎么的呢&#xff1f;LDO要不要并联二极管呢&#xff1f; 一般来说&#xff0c;LDO是不需要并联二极管的。 看下图第一个是典型电路&#xff0c;第二个是带可调节电压功能的LDO典型电路&#xff0c;从图里就可以…

设计模式-组合模式-笔记

“数据结构”模式 常常有一些组件在内部具有特定的数据结构&#xff0c;如果让客户程序依赖这些特定数据结构&#xff0c;将极大地破坏组件的复用。这时候&#xff0c;将这些特定数据结构封装在内部&#xff0c;在外部提供统一的接口&#xff0c;来实现与特定数据结构无关的访…

一起Talk Android吧(第五百五十四回:分享一个Retorfit使用错误的案例)

文章目录 1. 案例场景2. 案例现象3. 原因分析和解决方案3.1 原因分析3.2 解决方案4. 经验总结各位看官们大家好,上一回中咱们说的例子是"解析Retrofit返回的数据",本章回中将分享一个 Retrofit使用错误的案例。闲话休提,言归正转,让我们一起Talk Android吧! 1. …

三层交换机实现不同VLAN间通讯

默认时&#xff0c;同一个VLAN中的主机才能彼此通信&#xff0c;那么交换机上的VLAN用户之间如何通信&#xff1f; 要实现VLAN之间用户的通信&#xff0c;就必须借助路由器或三层交换机来完成。 下面以三层交换机为例子说明&#xff1a; 注意&#xff1a; 1.交换机与三层交换…

HWS-CTF-第七期山大站-inverse

文章目录 inversemainworkread_intread_n 思路onegadget exp 第一次真正意义上独立在比赛中做出题目来了&#xff0c;距离真正意义接触CTF-PWN差不多正好两个月。但由于不知道靶场要自己开而且端口每次自己打开会改&#xff0c;交flag稍微晚了些&#xff08;我太菜了&#xff0…

Java中锁的深入理解

目录 对象头的理解 Monitor&#xff08;锁&#xff09; 锁类型 偏向锁 偏向锁的优化机制 轻量级锁 重量级锁 对象头的理解 在32位Java虚拟机中普通对象的对象头是占用8个字节&#xff0c;其中4个字节为Mark Word。用来存储对象的哈希值&#xff0c;对象创建后在JVM中的…

【顺序表的实现】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 1. 数据结构相关概念 1、什么是数据结构 2、为什么需要数据结构&#xff1f; 2、顺序表 1、顺序表的概念及结构 1.1 线性表 2、顺序表分类 3、动态顺序表的实现 总…

ssm+vue的高校疫情防控管理系统(有报告)。Javaee项目,ssm vue前后端分离项目。

演示视频&#xff1a; ssmvue的高校疫情防控管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;ssm vue前后端分离项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结…

【C++入门】拷贝构造运算符重载

目录 1. 拷贝构造函数 1.1 概念 1.2 特征 1.3 常用场景 2. 赋值运算符重载 2.1 运算符重载 2.2 特征 2.3 赋值运算符 前言 拷贝构造和运算符重载是面向对象编程中至关重要的部分&#xff0c;它们C编程中的一个核心领域&#xff0c;本期我详细的介绍拷贝构造和运算符重载。 1. …

面向对象与面向过程的区别

面向对象 以对象为中心&#xff0c;把数据封装成为一个整体&#xff0c;其他数据无法直接修改它的数据&#xff0c;将问题分解成不同对象&#xff0c;然后给予对象相应的属性和行为。 面向过程 关注代码过程&#xff0c;直接一程序来处理数据&#xff0c;各模块之间有调用与…

OSI参考模型

目录 一. OSI参考模型的各层功能二. 网络排错三. 网络安全四. 实体、协议、服务和服务访问点SAP五. TCP IP体系结构 一. OSI参考模型的各层功能 \quad \quad \quad \quad 我们首先来看应用层实现的功能 每个字段的各种取值所代表的意思 \quad \quad 比如要保存的文件内容是ab…

OpenAI 董事会与 Sam Altman 讨论重返 CEO 岗位事宜

The Verge 援引多位知情人士消息称&#xff0c;OpenAI 董事会正在与 Sam Altman 讨论他重新担任首席执行官的可能性。 有一位知情人士表示&#xff0c;Altman 对于回归公司一事的态度暧昧&#xff0c;尤其是在他没有任何提前通知的情况下被解雇后。他希望对公司的治理模式进行重…

【开源】基于Vue.js的高校实验室管理系统的设计和实现

项目编号&#xff1a; S 015 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S015&#xff0c;文末获取源码。} 项目编号&#xff1a;S015&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、研究内容2.1 实验室类型模块2.2 实验室模块2.3 实…

Tomcat无法映射到activiti-app导致activiti无法启动页面

原因之一&#xff1a;JDK版本与Tomcat版本不匹配&#xff0c;jdk8 yyds 我使用的是JDK11&#xff0c;Tomcat是9.0的&#xff0c;都是最新的&#xff0c;但还是不行&#xff0c;最后JDK改为8&#xff0c;tomcat的cmd后台没有报错&#xff0c;activiti-pp也可以正常访问了,很神奇…

鸿蒙应用开发之打包与上架

一、概述 当您开发、调试完HarmonyOS应用/元服务&#xff0c;就可以前往AppGallery Connect申请上架&#xff0c;华为审核通过后&#xff0c;用户即可在华为应用市场获取您的HarmonyOS应用/元服务。 HarmonyOS会通过数字证书与Profile文件等签名信息来保证应用的完整性&#…