【Rust自学】4.5. 切片(Slice)

4.5.0. 写在正文之前

这是第四章的最后一篇文章了,在这里也顺便对这章做一个总结:

所有权、借用和切片的概念确保 Rust 程序在编译时的内存安全。 Rust语言让程序员能够以与其他系统编程语言相同的方式控制内存使用情况,但是当数据所有者超出范围时,让数据所有者自动清理该数据意味着您无需编写和调试额外的代码来获得这个控制权

看完这篇文章,相信你会由衷的感叹Rust所有权机制到底有多么神奇和先进。

喜欢的话别忘了点赞、收藏加关注哦,对接下来的教程有兴趣的可以关注专栏。谢谢喵!(=・ω・=)

4.5.1. 切片的特性

  • 1. 类型和结构

    • 切片类型的表示方式是:&[T]&mut [T],其中 T 是切片中元素的类型。
    • 不可变切片&[T],只允许读取操作。
    • 可变切片&mut [T],允许修改操作。
  • 2. 不拥有数据

    • 切片本质上是对底层数据的引用,因此它不拥有数据。
    • 切片的生命周期与底层数据一致,当底层数据被销毁时,切片也失效。

4.5.2. 字符串切片

以一道题为例:
编写一个函数,它接受字符串作为参数,它返回它在这个字符串中找到的第一个单词,如果函数没找到任何空格,那么整个字符串就被返回。

fn main() {
	let s = String::from("Hello world");
	let word_index = first_word(&s);
	println!("{}", word_index);
}
fn first_word(s:&String) -> usize {
	let bytes = s.as_bytes();
	for (i, &item) in bytes.iter().enumerate() {
		if item == b' ' {
			return i;
		} 
	}
	s.len()
}
  • 因为需要逐个元素地遍历String并检查值是否为空格,所以使用as_bytes方法将String转换为字节数组.
  • 迭代器在以后会讲到,现在只需要知道iter是一个方法,用来逐一获取集合中的每个元素。enumerate是一个工具,它在iter的基础上,为每个元素附加一个索引,并将结果作为元组返回。返回元组的第一个元素是索引,第二个元素是对该元素的引用

程序成功编译,输出是5。也就是Hello后边的空格的索引位置

我们现在有办法找出字符串中第一个单词末尾的索引,但是有一个问题。我们自己返回一个usize ,但它只是&String上下文中的一个有意义的数字。换句话说,因为它是与String不同的值,所以不能保证它在将来仍然有效

比如因为某些原因代码在调用first_word之后写了s.clean();这行来清空s,此时的word_index这个变量就没有意义了;也可以说,Rust编译器发现不了代码使用了s.clean()word_index仍然存在的错误,如果你在之后的代码中还使用了word_index去打印字符,那显然就会发生错误。

这类的API(或者叫函数设计)要求随时关注word_index的有效性,确保这个索引和这个String变量s它们之间的同步性。偏偏这类工作往往相当繁琐而且特别容易出错,所以针对这类问题Rust提供了字符串切片

字符串切片是指向字符串中一部分内容的引用。

在原字符串名前加上&代表对它的引用,在后加上[开始索引..结束索引],表示引用这个字符串的一部分。注意,[]内的区间是左闭右开,所以结束索引是切片终止位的下一个索引值。顺口溜:包左不包右。

fn main() {
	let s = String::from("hello world");
	let hello = &s[0..5];
	let world = &s[6..11];
}

在这个例子中把s从0到5的索引区间(包括0不包括5),也就是"Hello"这部分赋给了hello这个变量;把从6到11的索引区间(包括6不包括11),也就是"world"这个部分赋给了world这个变量
请添加图片描述

由图可见,world这个变量并不会独立于s而存在,这样使得编译器能够在编译过程中就发现许多潜在的问题。

当然,对于索引的写法,还有几种省略的方式:

let hello = &s[0..5];

这个变量是从索引0开始截取的,Rust允许这样的等价写法:

let hello = &s[..5];
let world = &s[6..11];

这个变量截取到了s的最后一个元素,Rust允许这样的等价写法:

let world = &s[6..];

如果想截取整个字符串,那就可以:

let whole = &s[..];

注意事项

  • 字符串切片的范围索引必须发生在有效的utf-8边界内
  • 如果尝试从一个多字节的字符中创建字符串切片,程序会报错并退出

重写代码

学了切片之后,就可以修改文章开头的代码来进一步优化了:

fn main() {
	let s = String::from("Hello world");
	let word = first_word(&s);
	println!("{}", word);
}
fn first_word(s:&String) -> &str {
	let bytes = s.as_bytes();
	for (i, &item) in bytes.iter().enumerate() {
		if item == b' ' {
			return &s[..i];
		} 
	}
	&s[..]
}
  • &str表示字符串切片

这个时候如果在word = first_word(&s);这一行之后加上s.clean();,Rust就能够发现错误并报错:

error[E0502]:cannot borrow `s` as mutable because it is also borrowed as immutable

因为在同一个作用域中出现了可变引用s.clean()和不可变引用&s,违反了借用规则
PS:s.clean()等价于clean(&mut s)

4.5.3. 字符串字面值就是切片

字符串字面值被直接存储在二进制程序之中,在程序运行时会被放入静态内存里

let s = "Hello, World!";

变量s的类型是&str,它是一个指向二进制程序特定位置的切片。&str不可用,所以字符串字面值也是不可变的。

4.5.4. 将字符串切片作为参数传递

fn first_word(s:&String) -> &str {

这是刚刚优化过的代码中声明函数的那一行,这种写法本身完全没有任何问题。但有经验的Rust开发者会使用&str作为s的参数类型,因为这样就可以同时接收String&str类型的参数了:

  • 如果你传入的的值是字符串切片,那么直接调用即可
  • 如果值类型是String,那么可以传入&String类型的实参,当函数参数需要&str而你传递的是&String时,Rust会隐式调用Deref,将&String转换为&str

定义函数时使用字符串切片来代替字符串引用会使APU更加通用,且不会损失任何功能。

根据它,还可以再进一步地优化之前的代码:

fn main() {
	let s = String::from("Hello world");
	let word = first_word(&s);
	println!("{}", word);
}
fn first_word(s:&str) -> &str {
	let bytes = s.as_bytes();
	for (i, &item) in bytes.iter().enumerate() {
		if item == b' ' {
			return &s[..i];
		} 
	}
	&s[..]
}

这行:

let word = first_word(&s);

也可以写成:

let word = first_word(&s[..]);

对于前者,Rust会隐式调用Deref,将&String转换为&str;后者是手动转换为&str类型

4.5.5. 其他类型的切片

fn main() {  
    let number = [1, 2, 3, 4, 5];  
    let num = &number[1..3];  
    println!("{:?}", num);  
}

数组也可以使用切片。num这个切片的本质就是存储了指向number中切片截取的起始点(这个例子中是索引为1的位置)的指针与长度的信息。

其输出是:

[2, 3]

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

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

相关文章

WEB入门——文件上传漏洞

文件上传漏洞 一、文件上传漏洞 1.1常见的WebShell有哪些?1.2 一句话木马演示1.2 文件上传漏洞可以利用需满足三个条件1.3 文件上传导致的危害 二、常用工具 2.1 搭建upload-labs环境2.2 工具准备 三、文件上传绕过 3.1 客户端绕过 3.1.1 实战练习 :upl…

【NLP高频面题 - Transformer篇】Transformer的位置编码是如何计算的?

【NLP高频面题 - Transformer篇】Transformer的位置编码是如何计算的? 重要性:★★★ NLP Github 项目: NLP 项目实践:fasterai/nlp-project-practice 介绍:该仓库围绕着 NLP 任务模型的设计、训练、优化、部署和应用…

[react 3种方法] 获取ant组件ref用ts如何定义?

获取ant的轮播图组件, 我用ts如何定义? Strongly Type useRef with ElementRef | Total TypeScript import React, { ElementRef } from react; const lunboRef useRef<ElementRef<typeof Carousel>>(null); <Carousel autoplay ref{lunboRef}> 这样就…

模型优化之知识蒸馏

文章目录 知识蒸馏优点工作原理示例代码 知识蒸馏优点 把老师模型中的规律迁移到学生模型中&#xff0c;相比从头训练&#xff0c;加快了训练速度。另一方面&#xff0c;如果学生模型的训练精度和老师模型差不多&#xff0c;相当于得到了规模更小的学生模型&#xff0c;起到模…

职业技能赛赛后心得

这是一位粉丝所要求的&#xff0c;也感谢这位粉丝对我的支持。 那么本篇文章我也是分成四个部分&#xff0c;来总结一下这次赛后心得。 赛中问题 那么这里的赛中问题不会只包含我所遇到的问题&#xff0c;也会包含赛中其他选手出现的问题。 那么首先我先说一下我在赛中遇到的…

基于springboot+vue实现的博物馆游客预约系统 (源码+L文+ppt)4-127

摘 要 旅游行业的快速发展使得博物馆游客预约系统成为了一个必不可少的工具。基于Java的博物馆游客预约系统旨在提供高效、准确和便捷的适用博物馆游客预约服务。本文讲述了基于java语言开发&#xff0c;后台数据库选择MySQL进行数据的存储。该软件的主要功能是进行博物馆游客…

前沿重器[57] | sigir24:大模型推荐系统的文本ID对齐学习

前沿重器 栏目主要给大家分享各种大厂、顶会的论文和分享&#xff0c;从中抽取关键精华的部分和大家分享&#xff0c;和大家一起把握前沿技术。具体介绍&#xff1a;仓颉专项&#xff1a;飞机大炮我都会&#xff0c;利器心法我还有。&#xff08;算起来&#xff0c;专项启动已经…

Dubbo 3.x源码(28)—Dubbo服务发布导出源码(7)应用级服务接口元数据发布

基于Dubbo 3.1&#xff0c;详细介绍了Dubbo服务的发布与引用的源码。 此前我们在Dubbo启动过程的DefaultModuleDeployer#startSync方法中&#xff0c;学习了Dubbo服务的导出exportServices方法和服务的引入referServices方法。 在这两个操作执行完毕之后&#xff0c;将会继续调…

电脑使用CDR时弹出错误“计算机丢失mfc140u.dll”是什么原因?“计算机丢失mfc140u.dll”要怎么解决?

电脑使用CDR时弹出“计算机丢失mfc140u.dll”错误&#xff1a;原因与解决方案 在日常电脑使用中&#xff0c;我们时常会遇到各种系统报错和文件丢失问题。特别是当我们使用某些特定软件&#xff0c;如CorelDRAW&#xff08;简称CDR&#xff09;时&#xff0c;可能会遇到“计算…

深入解读数据资产化实践指南(2024年)

本指南主要介绍了数据资产化的概念、目标和意义&#xff0c;以及实施数据资产化的过程。指南详细阐述了数据资产化的内涵&#xff0c;包括数据资产的定义、数据资产化的目标与意义&#xff0c;并介绍了数据资产化的过程包括业务数据化、数据资源化、数据产品化和数据资本化。 …

【算法篇】——数据结构中常见八大排序算法的过程原理详解

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、插入排序1.直接插入法2.希尔排序法 二、交换排序1. 冒泡排序2. 快速排序 三、选择排序1. 简单选择排序2. 堆排序 四、归并排序五、基数排序 前言 C数据结构…

仿闲鱼的二手交易小程序软件开发闲置物品回收平台系统源码

市场前景 闲置物品交易软件的市场前景广阔&#xff0c;主要基于以下几个方面的因素&#xff1a; 环保意识提升&#xff1a;随着人们环保意识的增强&#xff0c;越来越多的人开始关注资源的循环利用&#xff0c;闲置物品交易因此受到了广泛的关注。消费升级与时尚节奏加快&…

情报信息收集能力

红队专题-Web渗透之资产思路框架知识整理 钓鱼社工 钓鱼自动化zip域名ARP欺骗快捷方式ToolsburpsuiteApp 抓包ffuf模糊测试QingScanWiresharkCloudCFEn-Decodeffffffff0xInfodirbdirmapdirsearchdnsenum使用测试常规使用使用字典文件进行dns查询子域名暴力查询部分C类IP地址IP块…

ensp 关于acl的运用和讲解

ACL&#xff08;Access Control List&#xff0c;访问控制列表&#xff09;是一种常用于网络设备&#xff08;如路由器、交换机&#xff09;上的安全机制&#xff0c;用于控制数据包的流动与访问权限。ACL 可以指定哪些数据包允许进入或离开某个网络接口&#xff0c;基于不同的…

5、mysql的读写分离

主从复制 主从复制的含义 主从复制&#xff1a;在一个mysql的集群当中&#xff0c;至少3台&#xff0c;即主1台&#xff0c;从2台。 当有数据写入时&#xff0c;主负责写入本库&#xff0c;然后把数据同步到从服务器。 一定是在主服务器写入数据&#xff0c;从服务器的写入…

高质量配音如何影响游戏的受欢迎度

在游戏行业中&#xff0c;创造沉浸式、引人入胜且令人难忘的体验往往决定了游戏的成功或失败。在影响游戏流行度的众多因素中&#xff0c;配音脱颖而出&#xff0c;成为将叙事与玩家互动连接起来的重要元素。高质量的配音将游戏中的对白转化为游戏的活跃部分&#xff0c;让玩家…

鸿蒙-expandSafeArea使用

应用未使用setWindowLayoutFullScreen()接口设置窗口全屏布局时&#xff0c;默认使能组件安全区布局。可以使用expandSafeArea属性扩展安全区域属性进行调整 扩展安全区域属性原理 布局阶段按照安全区范围大小进行UI元素布局。布局完成后查看设置了expandSafeArea的组件边界&…

Java测试开发平台搭建(四)拦截器

1. 拦截器的作用及使用场景 能够在请求的生命周期的不同阶段进行拦截和处理。常见的使用场景包括&#xff1a;1. 日志记录&#xff1a;记录请求和响应的日志。 2. 权限验证&#xff1a;检查用户的登录状态、权限。 3. 性能监控&#xff1a;记录请求的处理时间&#xff0c;监控…

window安装TradingView

目录 下载安装包 修改文件后缀&#xff0c;解压 将K线换成国内涨红跌绿样式 下载安装包 https://www.tradingview.com/desktop/ 下载完成后是.msix格式文件 &#xff08;我在win10和win11的系统中尝试运行msix都没有成功&#xff0c;所以放弃直接双击运行msix&#xff…

电子应用设计方案70:智能挂钟系统设计

智能挂钟系统设计 一、引言 随着科技的不断发展&#xff0c;传统挂钟也逐渐向智能化方向演进。智能挂钟不仅能够准确显示时间&#xff0c;还具备多种实用功能和智能交互特性&#xff0c;为用户带来更便捷、丰富的体验。 二、系统概述 1. 系统目标 - 高精度显示时间&#xff0…