rust编程-rust所有权理解(chapter 4.3 Slice切片类型)

news2024/11/25 10:34:43

目录

3. 切片(Slice)类型

3.1 String slice(字符串切片)

3.2 其它切片


3. 切片(Slice)类型

切片可以用来获取一个集合中连续的元素序列,且切片是一种引用类型,因此不具有所有权。

如下是一个小的编程示例:编写一个函数,该函数接受一个字符串,并返回它在该字符串中找到的第一个单词。如果函数在字符串中没有找到空格,则整个字符串必须是一个单词,因此应该返回整个字符串。

在不使用Slice且不获取所有权时,可以使用只读的引用作为形参:

fn first_word(s: &String) -> ?

至于返回值,则可以返回首个单词的索引。如下:

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()
}

iter()返回的迭代器将在第13章节介绍,enumerate()包装成一个具有索引和索引值的可迭代元组,元组首元素是整型索引,次元素是字节值的引用。(i, &item)是一种对元组的解引用方式,第6章节将详细介绍。

上述实现返回首个单词的起始索引位置,usize类型。该实现的问题是,返回值只在String参数中有效,且一旦String参数的变量在调用函数中被改变,该返回值将不再有意义。

3.1 String slice(字符串切片)

语法如下示例:

    let s = String::from("hello world");

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

hello是整个字符串变量的一部分,通过[start_index..ending_index]的中括号语法形式来创建一个切片,切片中元素包含start_index,但不包含ending_index指向的元素。

从实现的角度:String切片数据结构中存放了Slice的起始位置,以及长度,原理如下图:

 

rust的..形式的范围语法,比较灵活方便:

1)可以省略起始索引

let s = String::from("hello");

let slice = &s[0..2];
let slice = &s[..2]; 

2)可以省略结束索引

let s = String::from("hello");

let len = s.len();

let slice = &s[3..len];
let slice = &s[3..];

3) 可以省略首尾双索引

let s = String::from("hello");

let len = s.len();

let slice = &s[0..len];
let slice = &s[..];

 

使用字符串切片重写first_word函数如下:

fn first_word(s: &String) -> &str {
    let bytes = s.as_bytes();

    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[0..i];
        }
    }

    &s[..]
}

从返回值可以看出,字符串切片的标识语法为 &str 。

first_word返回与底层数据结构绑定的切片(引用)类型,编译器会确保到String类型的引用是始终有效的(无论具有所有权的String变量指向的值是被释放或者修改),具有一个只读的切片引用时,原始String变量无法被修改后,或者至少修改后,无法再次通过切片引用进行访问。如下:

fn main() {
    let mut s = String::from("hello world");

    let word = first_word(&s);

    s.clear(); // error!

    println!("the first word is: {}", word);
}

编译器报错:

# cargo run
   Compiling ownership v0.1.0 (file:///projects/ownership)
error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable
  --> src/main.rs:18:5
   |
16 |     let word = first_word(&s);
   |                           -- immutable borrow occurs here
17 | 
18 |     s.clear(); // error!
   |     ^^^^^^^^^ mutable borrow occurs here
19 | 
20 |     println!("the first word is: {}", word);
   |                                       ---- immutable borrow later used here

For more information about this error, try `rustc --explain E0502`.
error: could not compile `ownership` due to previous error

clear()方法需要修改字符串,因此需要可变引用。由于word已经持有原始字符串的不可变引用,clear()的调用在rust中是不允许的,除非后面没有对word的访问。

  • String字符串常量本质是切片

二进制中存放的字符串常量,无法被修改。因此指向(引用)该常量值的的变量是不可变的。由此可知,以下代码:

let s = "Hello, world!";

s的实际类型是&str类型,本质是一个指向二进制特定位置的切片。而切片,是一种不可变的引用类型。

  • String切片作为参数

函数参数如果本身就是字符串常量,完全可以使用切片类型。函数声明从fn second_word(s: &String) -> &str,变为fn second_word(s: &str) -> &str。

这样做有一个好处:后者,既可以接收&String类型,也可以接收&str类型的值。

如果是String切片,可以直接传递给&str进行函数调用。

如果是String类型,既可以传递String的一个切片,也可以传递String的一个引用调用函数。这种灵活性实际使用了rust的“强制解引用”,这将在第15章节介绍。

如下:

fn main() {
    let my_string = String::from("hello world");

    // `first_word` works on slices of `String`s, whether partial or whole
    let word = first_word(&my_string[0..6]);
    let word = first_word(&my_string[..]);
    // `first_word` also works on references to `String`s, 
    // which are equivalent to whole slices of `String`s
    let word = first_word(&my_string);

    let my_string_literal = "hello world";

    // `first_word` works on slices of string literals, whether partial or whole
    let word = first_word(&my_string_literal[0..6]);
    let word = first_word(&my_string_literal[..]);

    // Because string literals *are* string slices already,
    // this works too, without the slice syntax!
    let word = first_word(my_string_literal);
}

3.2 其它切片

其它切片类型类似于String切片类型,整型切片如下:

let a = [1, 2, 3, 4, 5];

let slice = &a[1..3];

assert_eq!(slice, &[2, 3]);

上述切片类型是&[i32],如同String的切片类型,该切片类型也存放到数组首元素的引用,以及切片长度。其它的集合类型的切片,也同样是该类型。

Rust的所有权、借用和切片的概念,使得rust在在编译器确保其应用程序的内存安全性。如同其它编程语言,rust允许编程控制内存使用,且在拥有所有权的变量离开作用域时,自动对数据的所有权进行转移和清理。

“所有权”影响了rust编程的方方面面,后文会进一步涉及到。

关于作者:

犇叔,浙江大学计算机科学与技术专业,研究生毕业,而立有余。先后在华为、阿里巴巴和字节跳动,从事技术研发工作,资深研发专家。主要研究领域包括虚拟化、分布式技术和存储系统(包括CPU与计算、GPU异构计算、分布式块存储、分布式数据库等领域)、高性能RDMA网络协议和数据中心应用、Linux内核等方向。

专业方向爱好:数学、科学技术应用

关注犇叔,期望为您带来更多科研领域的知识和产业应用。

内容坚持原创,坚持干货有料。坚持长期创作,关注犇叔不迷路

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

day17_面向对象的三大特征之一(多态)

概述 多态是继封装、继承之后,面向对象的第三大特性。 生活中,比如求面积的功能,圆、矩形、三角形实现起来是不一样的。跑的动作,小猫、小狗和大象,跑起来是不一样的。再比如飞的动作,昆虫、鸟类和飞机&a…

【数字IC设计】Design Compiler入门

本博客参考自文章链接 本文以全加器为例,演示DC综合的流程。设计文件如下: module full_adder( input clk, input rstn, input [31:0] a_in, input [31:0] b_in, input c_in, output reg [31:0] sum_out, output reg c_out ); wire c_out_w; wire [31:0…

[vue学习笔记]数组+事件+v-model的使用

1.关于数组的使用以及常见的函数 (1)在队尾操作函数:push():追加,pop():删除 arr.push(9,8,7,6); 这种批量追加的方式也是被允许的 (2&#xf…

Gem5模拟器,关于Adding parameters to SimObjects and more events的一些问题记录(六)

目录 (1)为什么Gem是Python和C混合使用编程? (2)关于析构函数 创建类的时候一般都需要写上析构函数吗? (3)关于HelloObject和GoodbyeObject的先后后创建关系 (1&…

C# 类 字段 方法

一 现实中的实物抽象为类 类(class)最基本的要素是 ① 字段(field):变量; ② 方法(method):函数; class Person {public string name;public int age;public void SayHello(){Console.WriteLine("Hello!My name is"name);}public string Ge…

(Python)第2章-12 输出三角形面积和周长 (15 分)

Python解决输入三条边输出面积与周长1.问题2.解决思路代码在孤单的日子里,你单枪匹马奋斗的样子酷毙了。江客:时荒![在这里插入图片描述](https://img-blog.csdnimg.cn/85fc4495dcfc4578a8612a432d8045cd.png#pic_center)1.问题 本题要求编写程序&#…

Arduino Uno零基础入门学习笔记——变量与函数

文章目录一、创建变量二、函数三、delay的例子总结一、创建变量 int currentTemperature;整数型变量 变量名字 分号 使用驼峰命名法对进行命名 有些程序员喜欢全部小写,有些程序员喜欢用下划线,所以如果要写一个my name的变量,他们常用的写…

vue3中的propemit

状态是什么: 页面中要显示的东西,放在一个变量中,每次更改完值,就会被拦截,同时再重新渲染页面; 状态的对立面就是属性; 可以没有状态,那只能用父组件传过来的属性来自己用&#…

Saga 模式

Saga 模式目录概述需求:设计思路实现思路分析1.2.适用场景:3.缺点:4.Saga的实现:参考资料和推荐阅读Survive by day and develop by night. talk for import biz , show your perfect code,full busy,skip hardness,ma…

mysql数据恢复,mysql数据备份,详细聊聊mysql数据备份与恢复

文章目录写在前面数据备份与恢复1、全量备份模拟全量备份与恢复全量备份的缺点2、增量备份模拟增量备份与恢复增量备份注意事项总结写在前面 作为互联网开发人员来说,数据安全性一直排在第一位的重中之重。 mysql作为关系型数据库的一个巨头,其备份与恢…

从GNU/Linux看国产操作系统的安全可控性

作者:IT圈黎俊杰 在信创的春风下,做为IT基础软件三驾马车之一的操作系统,自然是国产化替代的重点,再加上一直以来被大家当成”免费RedHat Enterprise Linux“的CentOS因被redhat收购,并宣布于2021年12月31日起在停止维…

SpringBoot+SpringCloud微服务搭建全过程(一)

一:什么是SpringBoot 1. SpringBoot不是一个全新的框架,而是对Spring框架的一个封装。所以,以前Spring可以做的事情,现在用SpringBoot都可以做。 2. SpringBoot整合了很多优秀的框架,用来简化Spring应用搭建和开发过程&#xff…

java项目_第171期ssm房屋租赁系统_计算机毕业设计

java项目_第171期ssm房屋租赁系统_计算机毕业设计 【源码请到下载专栏下载】 今天分享的项目是《ssm房屋租赁系统》 该项目分为2个角色,管理员和用户。 用户可以浏览前台,查看房屋租赁情况,并且进行租赁。 还可以登录到后台,进行租赁订单管理…

【20221212】【每日一题】一和零

给你一个二进制字符串数组 strs 和两个整数 m 和 n 。 请你找出并返回 strs 的最大子集的长度,该子集中 最多 有 m 个 0 和 n 个 1 。 如果 x 的所有元素也是 y 的元素,集合 x 是集合 y 的 子集 。 思路:背包有两个维度:m、n。不…

【工作项目总结】交易系统

写工作项目总结的目的就是,明明自己工作中负责了一些模块也写了很多代码,解决了不少坑。但是面试的时候,总是记不起来在自己过往做的项目中,该项目的具体亮点难点分别是什么,有哪些令人印象深刻的问题,以及是如何解决它的。那么记录问题与解决思维就是我创作这个工作复盘…

C#连接蓝牙设备

看过各种博主的文章,发现基本上都是属于误人子弟的狗屁文章,踩过各种坑最终实现了此功能。 1.思路 电脑蓝牙和蓝牙模块配对连接 和我们平时正常连接蓝牙设备一样,需要先搜索附近的蓝牙设备,然后根据设备名来选择要连接的蓝牙模块…

VSCODE 系列(六)使用Plantuml插件制作UML类图

文章目录前言下载和安装支持文件格式支持绘制类型导出语法申报要素UML类图关系参考例子参考前言 软件设计中,有好几种图需要画,比如流程图、类图、组件图等,我知道大部分人画流程图一般都会用微软的visio绘制,visio画图有个不好的…

[附源码]计算机毕业设计高校实验室仪器设备管理系统Springboot程序

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: Springboot mybatis MavenVue等等组成,B/S模式…

Java学习笔记 --- 反射

一、反射机制 Java Reflection 1、反射机制允许程序在执行期间借助于 ReflectionAPI取得任何类的内部信息(比如成员变量,构造器,成员方法等),并能操作对象的属性及方法。反射在设计模式和框架底层都会用到 2、加载完…

数学基础从高一开始6、全称量词与存在量词

数学基础从高一开始6、全称量词与存在量词 目录 数学基础从高一开始6、全称量词与存在量词 全称量词 存在量词 1.判断命题的真假 2.判断命题的真假 阅读下列两组命题,语言上有什么特点? A组: (1)对任意一个x∈Z,2x1是整数; (2)每一个素…