Rust 语言从入门到实战
唐刚
Rust 语言中文社区联合创始人
5266 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 36 讲
Rust 语言从入门到实战
15
15
1.0x
00:00/00:00
登录|注册

04|字符串:对号入座,字符串其实没那么可怕!

你好,我是 Mike,今天我们来认识一下 Rust 中和我们打交道最频繁的朋友——字符串。
这节课我们把字符串单独拿出来讲,是因为字符串太常见了,甚至有些应用的主要工作就是处理字符串。比如 Web 开发、解析器等。而 Rust 里的字符串内容相比于其他语言来说还要多一些。是否熟练掌握 Rust 的字符串的使用,对 Rust 代码开发效率有很大影响,所以这节课我们就来重点攻克它。

可怕的字符串?

我们在 Rust 里常常会见到一些字符串相关的内容,比如下面这些。
String, &String,
str, &str, &'static str
[u8], &[u8], &[u8; N], Vec<u8>
as_str(), as_bytes()
OsStr, OsString
Path, PathBuf
CStr, CString
我们用一张图形象地表达 Rust 语言里字符串的复杂性。
有没有被吓到?顿时不想学了,Rust 从入门到放弃,第一次 Rust 旅程到此结束。
且慢且慢,先不要盖棺定论。仔细想一想 Rust 中的字符串真的有这么复杂吗?这些眼花缭乱的符号到底是什么?我来给你好好分析一下。
首先,我们来看 C 语言里的字符串。图里显示,C 中的字符串统一叫做 char *,这确实很简洁,相当于是统一的抽象。但是这个统一的抽象也付出了代价,就是丢失了很多额外的信息
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

Rust中的字符串处理是开发中常见的任务。本文通过介绍不同类型的字符串及其特点,帮助读者更好地理解Rust中的字符串处理。文章详细介绍了`String`、`&String`、`str`、`&str`等不同类型的字符串表示及其内存结构,以及与字符串相关的`[u8]`、`&[u8]`、`&[u8; N]`、`Vec<u8>`等类型的比较。此外,还介绍了切片的概念和使用方法,以及`as_str()`、`as_bytes()`、`as_slice()`等方法的使用。文章还介绍了隐式引用类型转换、字节串转换成字符串、字符串切割成字符数组、其他字符串相关类型以及Parse方法。通过本文的阅读,读者可以快速了解Rust中字符串的各种类型及其使用方法,为日后的Rust开发提供了重要参考。文章内容丰富,涵盖了Rust中字符串处理的方方面面,对于想要深入了解Rust字符串处理的读者来说,是一篇不可多得的技术指南。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Rust 语言从入门到实战》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(22)

  • 最新
  • 精选
  • 一个人旅行
    1.String类型为struct类型,实现了Deref特征。 2.当String类型调用chars方法是,编译器会检查String类型是否实现了chars方法,检查项包括self,&self,&mut self 3.如果都没有实现chars方法,编译器则调用deref方法解引用(智能指针),得到str,此时编译器才会调用chars方法,也就是可以调用str实现的所有方法

    作者回复: 👍

    2023-10-27归属地:北京
    28
  • eriklee
    什么时候用to_owned(),什么时候用to_string()呢?

    作者回复: to_owned() 是更通用的,目的就是在只能拿到引用的情况下获得所有权(通过克隆一份资源)。to_string() 就只是转成字符串而已,这两个用法重叠,但是不完全相同。

    2023-10-31归属地:北京
    2
    7
  • plh
    [思考题]: 实际上 str 上的所有方法,String 都能调用. 这个地方 对初学者需要 循序渐进地理解: 1. 最开始接触 引用的概念是 rust book 中文版 中有句话, “而更有经验的 Rustacean 会编写出示例 4-9 中的签名”, 使用的函数 签名 从 &String 转换成 &str, 见链接 https://kaisery.github.io/trpl-zh-cn/ch04-03-slices.html#%E5%AD%97%E7%AC%A6%E4%B8%B2-slice-%E4%BD%9C%E4%B8%BA%E5%8F%82%E6%95%B0, 当时觉得很神奇, 更有经验的 Rustacean, 不就是 自己的当前目标吗. 2. 随着 继续深入下去, 自然接触到了 Deref 运算符, 也就是这个地方 这个地方实现上会调用 s.defer() , 这个地方 就是一个本质. 3. Rust 既然很受广大程序员的欢迎还是有原因的, 有很多符合 "人体工程学"的特性, 其中就有 automatic referencing and dereferencing, [运算符到哪去了], 见链接 https://kaisery.github.io/trpl-zh-cn/ch05-03-method-syntax.html#--%E8%BF%90%E7%AE%97%E7%AC%A6%E5%88%B0%E5%93%AA%E5%8E%BB%E4%BA%86 4. 本课的 [隐式引用类型转换], 个人觉得 有点 歧义,可能会引起后续的麻烦. 首先 Rust 不允许自动类型隐式转换,从这个角度来说, Rust 就属于强类型语言(什么是 强类型语言这个是争议的,可以暂时不管).其次, 如果换成 automatic referencing and dereferencing(自动 引用 和解引用) ,这个说法 就更严谨一点. 个人愚见,欢迎老师同学指正.

    作者回复: 很棒的思考。第3点,从初学者理解的角度来看,这个类型的转换确实是 “自动” “隐式”完成的,只不过这个隐式不是语言内置的,而是在标准库中通过Deref trait完成的。这样处理的原因在于,引入自动引用和解引用,会给初学者带来新的理解负担。官方book在这个问题上处理得并不好。另外,rust中并没有禁用 隐式 这个词。

    2023-11-01归属地:四川
    4
  • 二夕
    在 Rust 中,String 是一个可变的字符串类型,是用结构体定义的,而且实现了 Deref trait。str 是一个不可变的字符串切片类型。当调用一个 str 上的方法时,实际上就是通过 Deref 的自动转换机制(解引用),将 String 转换为对应的 str 切片,从而可以调用 str 上的方法。

    作者回复: 非常清晰!👍

    2023-11-01归属地:浙江
    2
  • superggn
    简单理解: 有个东西叫 deref coercion 在类型A上调函数的时候如果 typeA 有对应方法, 直接调; 如果没有对应方法, 就往下着 typeA 有没有实现 deref 如果没实现, GG 如果 typeA 能 deref 到 typeRoot, 那么在 typeRoot 里寻找有没有对应方法 如果 typeRoot 里没有方法, 继续看 typeRoot 里有没有对应方法, 递归往下走

    作者回复: 👍真棒

    2023-12-13归属地:北京
    1
  • Citroen
    同过学习我理解的是下面这样,有不对请老师指正,谢谢。 Rust中char是用于存放unicode单个字符的类型(最多四个字节)。 String类型只能放在堆上,通过引用所有权的形式和变量绑定,它的存储方式不是简单的char数组,而是utf8编码的字节序列,所以单独取这个序列的某一段切片,不一定能解析出具体的字符(程序里的String[a..b],这里的a和b已经是经过特殊处理的保证截取的有效性),如果能取得有效的序列片段那就是str类型,但是程序里凡是用到绑定str类型变量的地方,则必须都是引用形式存在的(&str),因为str是引用的原始片段的那段真是数据,而&str类型是一个FatPointer,它包括引用目标的起始地址和长度,所以str和&str是完全两个不同的概念。 u8就是一个存储0到255大小的类型,因为一个字节就是8位,所以[u8, N]可以看做是程序的任何类型数据的二进制表示形式。 不管是[T], Vec<T>,甚至Vec<Vec<Vec<String>>>只要Vec实现了Deref特性都并且Vec上没有的,都会层层解到最后需要的类型的方法上。

    作者回复: 非常棒的思考! 0. “Rust中char是用于存放unicode单个字符的类型(最多四个字节)。”。Rust语言里面的char就是占用4个字节,固定的。 1. “程序里的String[a..b],这里的a和b已经是经过特殊处理的保证截取的有效性”。对的。 fn main() { let s = "abcdefghijk".to_string(); let a = &s[..5]; let s = "我爱中国".to_string(); let a = &s[..5]; } Running `target/debug/playground` thread 'main' panicked at src/main.rs:7:15: byte index 5 is not a char boundary; it is inside '爱' (bytes 3..6) of `我爱中国` 2. “所以str和&str是完全两个不同的概念”。对的。 3. "所以[u8; N]可以看做是程序的任何类型数据的二进制表示形式。" 。对的。 4. “不管是[T], Vec<T>,甚至Vec<Vec<Vec<String>>>只要Vec实现了Deref特性都并且Vec上没有的,都会层层解到最后需要的类型的方法上。”。对的,适用于任何类型。

    2023-11-15归属地:辽宁
    2
    1
  • -Hedon🍭
    思考题:在 Rust 中,trait 是一种定义共享行为的机制。当一个类型实现了某个 trait,它就可以使用该 trait 中定义的方法。String 类型实现了 str 类型的 trait,因此可以调用 str 类型上定义的方法。

    作者回复: 这句话不对:“String 类型实现了 str 类型的 trait”。String 通过实现Deref trait,目标为 str,因此可以调用 str 类型上定义的方法。

    2023-10-30归属地:湖北
    1
  • Lucas Lin
    对「String、&String、str、&str、&'static str 之间的关系图」有个疑问。&'static str 和 &str 都是切片引用,那 &'static str 是不是也应该和 &str 一样有个引用范围的图示?范围是整个字符串("I am a superman.")。

    作者回复: 这个建议非常棒,我画下面部分的时候,没有想到这一点。这样更严谨,感谢指正。

    2023-10-29归属地:中国台湾
    1
  • 舒灿
    感谢评论区各位大哥的回复,不然看了这篇真要从入门到放弃了

    作者回复: 哪点要放弃?你如果放弃了说明我的讲法还需要改进!

    2024-01-17归属地:四川
    3
  • 飞了的鸭子被煮了
    let s = String::from("value"); let s2 = s[..]; 这里如果这样声明,会报错,说str没有实现size,如果加了&就没问题,是不是说明 & 把 String的所有权借用过来后,对于 str 来说就能获取到 size,从而不报错了呢

    作者回复: 定义变量是在栈上的handle,必须是固定尺寸的,因此要加个&符号取引用

    2024-01-16归属地:美国
收起评论
显示
设置
留言
22
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部