当前位置: 首页 > news >正文

【rCore OS 开源操作系统】Rust 字符串(可变字符串String与字符串切片str)

【rCore OS 开源操作系统】Rust 语法详解: Strings

前言

这次涉及到的题目相对来说比较有深度,涉及到 Rust 新手们容易困惑的点。

这一次在直接开始做题之前,先来学习下字符串相关的知识。

Rust 的字符串

Rust中“字符串”这个概念涉及多种类型,这里介绍题目中涉及到的两种的字符串类型:&str(字符串切片,slice)和String(可变字符串)。

String 可变字符串

存储在堆中,并且是可变的——是和其他大部分主流编程语言类似的概念。

示例:

let mut s: String = String::from("hello");
s.push_str(", world"); // “hello, world”,是的这里就是可变的

&str 字符串切片

那么,什么是切片呢?

既然 String 被我们称为可变字符串,这里也就暗示字符串切片是不可变的immutable)。

同时,它是存储在栈中的。

来看看如何产生字符串切片:

// 字面量是一种切片
let s1 = "Hello, World"; 
// 完整的写法其实是 let s1: &str = "Hello, World"; // 也可以从可变字符串中切割
let string:String = String::from("wow,amazing");  
let s2 = string[0..3]; // 左开右闭,正好就是 “wow”

可以从切片中得到可变字符串:

let string1 = String::from("hello");
let string2 = "hello".to_string();

这里也不很啰嗦,很直观地就认识了两种字符串在形式上的区别。

那再来看看本质。

所有权、引用与借用

要搞懂字符串的本质,还得回到所有权、引用和借用三个概念上。

所有权

这里是一段官话:

所有权是 Rust 中的核心概念之一,它决定了数据的生命周期和内存管理方式。每个值在 Rust 中都有一个拥有它的变量,称为“所有者”。

所有权有三个特性:

  • 唯一所有者:一个值在同一时间内只能有一个所有者。
  • 所有权转移:当把一个所有者的值传递给另一个变量时,所有权会被转移。
  • 自动释放内存:当一个值的所有者离开作用域时,该值所占用的内存会被自动释放

第一次看估计不太理解,但是没关系,直接来对照代码再看一次:

这里就当作是一个代码拟人小故事来看

// 现在变量 s 是 String::from("hello") 的所有者
let s = String::from("hello");
// 直接赋值,现在 String::from("hello") 到了 t 的手中,t 是新的所有者,而 s 不再是了,s 会被释放掉!
// 这里就体现了三个特性,“唯一所有者”,“所有权转移”和“自动释放内存”。
let t = s; 
//  这里会编译失败,因为 s 已经不再有效println!("{}", s); // 报错

所有权的作用是什么?

这个问题有有更广而全面的回答,此处只简单说明最核心的一点

内存安全,所有权机制的存在避免了许多常见的内存错误,如悬空引用双重释放等问题——因为一个引用永远都是有值的,并且同一时刻只有一个指针能够操作值,而且在值指针失效时会自动销毁。

引用和借用

Rust 中的引用是(直接)基于指针实现的,可以看作是别名

而 JavaScript 等语言并不是直接基于指针实现的,而是依靠对象的引用实现的。
当然了,对象的引用的本质其实也还是可以基于指针——所以这里提到了“直接”一词。

基于指针是什么意思呢?

对于 String 类型,它的定义差不多是这样:

struct MyString {ptr: *const u8, // 一个指针len: usize, // 字符串长度, 是当前字符串中字符的数量cap: usize, // 字符串的最大容量,指最多可以容纳多少长度
}

那别名是什么意思呢?

这里我们也拟人地来解释下:

// a 和 b 是两个人,只是名字相同。
let a = 1;
let b = 1;// s 和 r 是同一个人,只是有两个名字
let mut s = String::from("hello");
let r = &s; // r 是 s 的引用

当然,这里的写法中, r 是不可以去修改值的。
如果需要修改,那么要这样写:

let r = &mut s; // r 是 s 的引用

那什么是借用呢?
这里的官话就是:

借用是值,允许使用一个变量的值,而不改变所有权。

其实,借用就是 Rust 的引用的一个特性。

来看下述代码(魔改自《Rust 高级程序设计》):

fn main() {let s1 = String::from("hello");// 传入一个 s1 的引用,不会改变所有权。// 而 s1 的所有权没有转移,就意味着 s1 不会被销毁,在后面可以接着用。let len = calculate_length(&s1); // 使用 s1 不报错呢println!("The length of '{}' is {}.", s1, len);
}fn calculate_length(s: &String) -> usize {s.len()
}

练习题

Strings1

题目
// strings1.rs
//
// Make me compile without changing the function signature!
//
// Execute `rustlings hint strings1` or use the `hint` watch subcommand for a
// hint.// I AM NOT DONEfn main() {let answer = current_favorite_color();println!("My current favorite color is {}", answer);
}fn current_favorite_color() -> String {"blue"
}
题解

有了上面的解释后,这里做起来就简单一些了。
这里的考点也就是区分可变字符串 String 和字符串切片 &str

// strings1.rs
//
// Make me compile without changing the function signature!
//
// Execute `rustlings hint strings1` or use the `hint` watch subcommand for a
// hint.fn main() {let answer = current_favorite_color();println!("My current favorite color is {}", answer);
}fn current_favorite_color() -> String {// 两种写法都可以// "blue".to_string()String::from("blue")
}

Strings2

题目
// strings2.rs
//
// Make me compile without changing the function signature!
//
// Execute `rustlings hint strings2` or use the `hint` watch subcommand for a
// hint.fn main() {let word = String::from("green"); // Try not changing this line :)if is_a_color_word(&word) {println!("That is a color word I know!");} else {println!("That is not a color word I know.");}
}fn is_a_color_word(attempt: &String) -> bool {attempt == "green" || attempt == "blue" || attempt == "red"
}
题解

在上文的字符串的知识点梳理中,其实已经给出了类似的代码了。

// strings2.rs
//
// Make me compile without changing the function signature!
//
// Execute `rustlings hint strings2` or use the `hint` watch subcommand for a
// hint.fn main() {let word = String::from("green"); // Try not changing this line :)if is_a_color_word(&word) {println!("That is a color word I know!");} else {println!("That is not a color word I know.");}
}fn is_a_color_word(attempt: &String) -> bool {attempt == "green" || attempt == "blue" || attempt == "red"
}

Strings3

题目
// strings3.rs
//
// Execute `rustlings hint strings3` or use the `hint` watch subcommand for a
// hint.// I AM NOT DONEfn trim_me(input: &str) -> String {// TODO: Remove whitespace from both ends of a string!???
}fn compose_me(input: &str) -> String {// TODO: Add " world!" to the string! There's multiple ways to do this!???
}fn replace_me(input: &str) -> String {// TODO: Replace "cars" in the string with "balloons"!???
}#[cfg(test)]
mod tests {use super::*;#[test]fn trim_a_string() {assert_eq!(trim_me("Hello!     "), "Hello!");assert_eq!(trim_me("  What's up!"), "What's up!");assert_eq!(trim_me("   Hola!  "), "Hola!");}#[test]fn compose_a_string() {assert_eq!(compose_me("Hello"), "Hello world!");assert_eq!(compose_me("Goodbye"), "Goodbye world!");}#[test]fn replace_a_string() {assert_eq!(replace_me("I think cars are cool"), "I think balloons are cool");assert_eq!(replace_me("I love to look at cars"), "I love to look at balloons");}
}
题解

这个题目还是有点难度,考察 Rust 常用的字符串操作 API。
这里我根据题目的提示查阅了官方文档:
Rust字符串操作方法: trim
然后还要注意,有的方法挂在是字符串切片,有的则挂在可变字符串上。
所以在操作它们的时候,还得先转化。

// strings3.rs
//
// Execute `rustlings hint strings3` or use the `hint` watch subcommand for a
// hint.fn trim_me(input: &str) -> String {let string = input.to_string();string.trim().to_string()
}fn compose_me(input: &str) -> String {let mut string = input.to_string();// 这里有个坑点是,push_str 改变的是在原来的值上进行修改,而返回值是一个空的元组string.push_str(" world!");string// 还有一种比较奇技淫巧:// format!("{} world!", input)
}fn replace_me(input: &str) -> String {let str = input.replace("cars", "balloons");str.to_string()
}#[cfg(test)]
mod tests {use super::*;#[test]fn trim_a_string() {assert_eq!(trim_me("Hello!     "), "Hello!");assert_eq!(trim_me("  What's up!"), "What's up!");assert_eq!(trim_me("   Hola!  "), "Hola!");}#[test]fn compose_a_string() {assert_eq!(compose_me("Hello"), "Hello world!");assert_eq!(compose_me("Goodbye"), "Goodbye world!");}#[test]fn replace_a_string() {assert_eq!(replace_me("I think cars are cool"),"I think balloons are cool");assert_eq!(replace_me("I love to look at cars"),"I love to look at balloons");}
}

相关文章:

【rCore OS 开源操作系统】Rust 字符串(可变字符串String与字符串切片str)

【rCore OS 开源操作系统】Rust 语法详解: Strings 前言 这次涉及到的题目相对来说比较有深度,涉及到 Rust 新手们容易困惑的点。 这一次在直接开始做题之前,先来学习下字符串相关的知识。 Rust 的字符串 Rust中“字符串”这个概念涉及多种类型&…...

远程过程调用RPC知识科普

文章目录 什么是RPCRPC的基本原理RPC的应用场景RPC的优势常见的RPC框架 常见的RPC协议1. gRPC2. Apache Thrift3. Dubbo4. JSON-RPC5. XML-RPC6. SOAP springboot环境下常用的RPC框架使用1. Apache Dubbo2. Apache Thrift3. gRPC4. Spring Cloud OpenFeign 什么是RPC RPC&…...

Java - LeetCode面试经典150题 - 区间 (三)

区间 228. 汇总区间 题目 给定一个 无重复元素 的 有序 整数数组 nums 。 返回 恰好覆盖数组中所有数字 的 最小有序 区间范围列表 。也就是说,nums 的每个元素都恰好被某个区间范围所覆盖,并且不存在属于某个范围但不属于 nums 的数字 x 。 列表中…...

NVIDIA网卡系列之ConnectX-6 DX规格信息(200G-PCIe 4.0x16-8PF1000VF-2019年发布)

背景 NVIDIA ConnectX-6是最大支持200G的产品,有DX LX等系列。LX一般是25G比较便宜。 核心关键点 200GbpsPCIe 4.0,最大lane: x16 (4.0的lane速 16GT/s * 16 256T/s,所以支持的是200G的网卡用PCIe4.0)QSFPPF,VF数量&#xff1…...

【案例】平面云

教程案例视频:Unity Shader Graph - 云教程 开发平台:Unity 2022 开发工具:Unity ShaderGraph   一、效果展示 二、ShaderGraph 路线图 三、案例分析 核心思路:使用 Noise(噪声)模拟云层状态   3.1 说明…...

测试用例的进阶二

1. 按开发阶段划分 1.1 测试金字塔 从上到下,对于测试人员代码就是要求越来越低; 从下到上,越来越靠近用户; 从下到上,定位问题的成本越来越高; 1.2 单元测试(Unit Testing) 单元测试是对软件组成单元进…...

zotero WebDAV同步忘记密码

https://www.jianguoyun.com/#/safety 找到应用密码...

如何在 SQL 中创建一个新的数据库?

在SQL中创建一个新的数据库,首先你需要有一个可以执行SQL语句的环境。 这通常意味着你已经有了一个数据库管理系统(DBMS),如MySQL、PostgreSQL、Oracle或Microsoft SQL Server等。 不同的DBMS可能有不同的细节,但基本…...

《Linux从小白到高手》理论篇:Linux的进程管理详解

本篇将介绍Linux的进程管理相关知识,并将深入介绍Linux的进程间相互通信。 进程就是运行中的程序,一个运行着的程序,可能有多个进程。 比如Oracle DB,启动Oracle实例服务后,就会有多个进程。 Linux进程分类 在 Linux…...

【Qt】控件概述(3)—— 显示类控件

显示类控件 1. QLabel——标签1.1 setPixmap设置图片1.2 setAlignment设置文本对齐方式1.3 setWordWrap设置自动换行1.4 setIndent设置缩进1.5 setMargin设置边距1.6 body 2. QLCDNumber2.1 使用QTimer实现一个倒计时效果2.2 使用循环的方式实现倒计时 3. QProgressBar——进度…...

数据库管理-第247期 23ai:全球分布式数据库-Schema对象(20241004)

数据库管理247期 2024-10-04 数据库管理-第247期 23ai:全球分布式数据库-Schema对象(20241004)1 分区、表空间和Chunk(块)2 表空间组3 分片表4 分片表族5 复制表6 在所有分片上创建的非表对象总结 数据库管理-第247期 …...

Docker搭建一款开源的文档管理系统

1.系统介绍 Wizard是一款开源的文档管理系统,它支持多种格式类型的文档管理,包括Markdown、Swagger和Table,以适应不同场景和需求下的文档管理需求。 1.1功能特点 开源免费:Wizard是一款完全免费的开源项目,用户可以…...

软件验证与确认实验一:静态分析

目录 1. 实验目的及要求.................................................................................................... 3 2. 实验软硬件环境.................................................................................................... 3 …...

基于SpringBoot+Vue的高校运动会管理系统

作者:计算机学姐 开发技术:SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等,“文末源码”。 专栏推荐:前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏:…...

什么东西可以当做GC Root,跨代引用如何处理?

引言 在Java的垃圾回收机制中,GC Root(Garbage Collection Root,垃圾回收根)是垃圾回收器判断哪些对象是可达的,哪些对象可以被回收的起点。GC Root通过遍历对象图,标记所有可达的对象,而那些不…...

Python深度学习:从神经网络到循环神经网络

Python深度学习:从神经网络到循环神经网络 目录 ✨ 神经网络基础 1.1 🔍 前向传播与反向传播🎨 卷积神经网络(CNN) 2.1 🖼️ 图像分类任务的实现 2.2 🚀 常用架构(LeNet、VGG、Res…...

C++输⼊输出

1.<iostream> 是 Input Output Stream 的缩写&#xff0c;是标准的输⼊、输出流库&#xff0c;定义了标准的输⼊、输 出对象 2.std::cin 是 istream 类的对象&#xff0c;它主要⾯向窄字符&#xff08;narrow characters (of type char)&#xff09;的标准输 ⼊流。 3…...

卡码网KamaCoder 117. 软件构建

题目来源&#xff1a;117. 软件构建 C题解&#xff08;来源代码随想录&#xff09;&#xff1a;拓扑排序&#xff1a;给出一个 有向图&#xff0c;把这个有向图转成线性的排序。拓扑排序也是图论中判断有向无环图的常用方法。 拓扑排序的过程&#xff0c;其实就两步&#xff1…...

Acwing 线性DP

状态转移方程呈现出一种线性的递推形式的DP&#xff0c;我们将其称为线性DP。 Acwing 898.数字三角形 实现思路&#xff1a; 对这个三角形的数字进行编号&#xff0c;状态表示依然可以用二维表示&#xff0c;即f(i,j),i表示横坐标&#xff08;横线&#xff09;&#xff0c;j表…...

Docker面试-24年

1、Docker 是什么&#xff1f; Docker一个开源的应用容器引擎&#xff0c;是实现容器技术的一种工具&#xff0c;让开发者可以打包他们的应用以及环境到一个镜像中&#xff0c;可以快速的发布到任何流行的操作系统上。 2、Docker的三大核心是什么? 镜像&#xff1a;Docker的…...

conda相比python好处

Conda 作为 Python 的环境和包管理工具&#xff0c;相比原生 Python 生态&#xff08;如 pip 虚拟环境&#xff09;有许多独特优势&#xff0c;尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处&#xff1a; 一、一站式环境管理&#xff1a…...

idea大量爆红问题解决

问题描述 在学习和工作中&#xff0c;idea是程序员不可缺少的一个工具&#xff0c;但是突然在有些时候就会出现大量爆红的问题&#xff0c;发现无法跳转&#xff0c;无论是关机重启或者是替换root都无法解决 就是如上所展示的问题&#xff0c;但是程序依然可以启动。 问题解决…...

MongoDB学习和应用(高效的非关系型数据库)

一丶 MongoDB简介 对于社交类软件的功能&#xff0c;我们需要对它的功能特点进行分析&#xff1a; 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具&#xff1a; mysql&#xff1a;关系型数据库&am…...

MySQL用户和授权

开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务&#xff1a; test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...

精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南

精益数据分析&#xff08;97/126&#xff09;&#xff1a;邮件营销与用户参与度的关键指标优化指南 在数字化营销时代&#xff0c;邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天&#xff0c;我们将深入解析邮件打开率、网站可用性、页面参与时…...

【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)

本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...

Python 高效图像帧提取与视频编码:实战指南

Python 高效图像帧提取与视频编码:实战指南 在音视频处理领域,图像帧提取与视频编码是基础但极具挑战性的任务。Python 结合强大的第三方库(如 OpenCV、FFmpeg、PyAV),可以高效处理视频流,实现快速帧提取、压缩编码等关键功能。本文将深入介绍如何优化这些流程,提高处理…...

pgsql:还原数据库后出现重复序列导致“more than one owned sequence found“报错问题的解决

问题&#xff1a; pgsql数据库通过备份数据库文件进行还原时&#xff0c;如果表中有自增序列&#xff0c;还原后可能会出现重复的序列&#xff0c;此时若向表中插入新行时会出现“more than one owned sequence found”的报错提示。 点击菜单“其它”-》“序列”&#xff0c;…...

数据库正常,但后端收不到数据原因及解决

从代码和日志来看&#xff0c;后端SQL查询确实返回了数据&#xff0c;但最终user对象却为null。这表明查询结果没有正确映射到User对象上。 在前后端分离&#xff0c;并且ai辅助开发的时候&#xff0c;很容易出现前后端变量名不一致情况&#xff0c;还不报错&#xff0c;只是单…...

【深尚想】TPS54618CQRTERQ1汽车级同步降压转换器电源芯片全面解析

1. 元器件定义与技术特点 TPS54618CQRTERQ1 是德州仪器&#xff08;TI&#xff09;推出的一款 汽车级同步降压转换器&#xff08;DC-DC开关稳压器&#xff09;&#xff0c;属于高性能电源管理芯片。核心特性包括&#xff1a; 输入电压范围&#xff1a;2.95V–6V&#xff0c;输…...