当前位置: 首页 > 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的…...

AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

家政维修平台实战20:权限设计

目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系&#xff0c;主要是分成几个表&#xff0c;用户表我们是记录用户的基础信息&#xff0c;包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题&#xff0c;不同的角色&#xf…...

零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)

本期内容并不是很难&#xff0c;相信大家会学的很愉快&#xff0c;当然对于有后端基础的朋友来说&#xff0c;本期内容更加容易了解&#xff0c;当然没有基础的也别担心&#xff0c;本期内容会详细解释有关内容 本期用到的软件&#xff1a;yakit&#xff08;因为经过之前好多期…...

SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题

分区配置 (ptab.json) img 属性介绍&#xff1a; img 属性指定分区存放的 image 名称&#xff0c;指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件&#xff0c;则以 proj_name:binary_name 格式指定文件名&#xff0c; proj_name 为工程 名&…...

Linux 内存管理实战精讲:核心原理与面试常考点全解析

Linux 内存管理实战精讲&#xff1a;核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用&#xff0c;还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...

给网站添加live2d看板娘

给网站添加live2d看板娘 参考文献&#xff1a; stevenjoezhang/live2d-widget: 把萌萌哒的看板娘抱回家 (ノ≧∇≦)ノ | Live2D widget for web platformEikanya/Live2d-model: Live2d model collectionzenghongtu/live2d-model-assets 前言 网站环境如下&#xff0c;文章也主…...

go 里面的指针

指针 在 Go 中&#xff0c;指针&#xff08;pointer&#xff09;是一个变量的内存地址&#xff0c;就像 C 语言那样&#xff1a; a : 10 p : &a // p 是一个指向 a 的指针 fmt.Println(*p) // 输出 10&#xff0c;通过指针解引用• &a 表示获取变量 a 的地址 p 表示…...

C++_哈希表

本篇文章是对C学习的哈希表部分的学习分享 相信一定会对你有所帮助~ 那咱们废话不多说&#xff0c;直接开始吧&#xff01; 一、基础概念 1. 哈希核心思想&#xff1a; 哈希函数的作用&#xff1a;通过此函数建立一个Key与存储位置之间的映射关系。理想目标&#xff1a;实现…...

6️⃣Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙

Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙 一、前言:离区块链还有多远? 区块链听起来可能遥不可及,似乎是只有密码学专家和资深工程师才能涉足的领域。但事实上,构建一个区块链的核心并不复杂,尤其当你已经掌握了一门系统编程语言,比如 Go。 要真正理解区…...

解析两阶段提交与三阶段提交的核心差异及MySQL实现方案

引言 在分布式系统的事务处理中&#xff0c;如何保障跨节点数据操作的一致性始终是核心挑战。经典的两阶段提交协议&#xff08;2PC&#xff09;通过准备阶段与提交阶段的协调机制&#xff0c;以同步决策模式确保事务原子性。其改进版本三阶段提交协议&#xff08;3PC&#xf…...