一句话总结
字符串的日常操作--拼接,查找,替换,解析--在 Rust 里都围绕"是否需要新分配内存"展开.
而 format! 宏与 Display/Debug 两个特征,则决定了一个类型如何变成人类可读的文本.
前置回顾
前两篇建立了基础:第 01 篇讲 String 与 &str 的所有权分工,第 02 篇讲 UTF-8 变长编码和遍历方式.本篇在此之上,展开字符串的日常操作 API 和格式化体系.
拼接:四种方式与各自代价
把两个字符串接起来,Rust 提供了若干写法,差别主要在所有权和分配次数上.
|
+ 运算符的所有权陷阱
a + &b 的真实签名是 fn add(self, &str) -> String:它消耗左侧 String 的所有权,复用其堆缓冲区,再追加右侧.所以拼接后 a 不可再用,而 b 因为是借用仍然可用.
这种不对称常让新手困惑--需要保留两个原串时,用 format! 更省心.
循环里拼接要预留容量
在循环中反复 push_str 时,String 可能多次重新分配并搬迁堆数据.若能预估最终大小,先用 String::with_capacity(n) 一次性申请,可避免中途扩容,是常见的性能优化手段.
查找与判断
这一类方法都是只读的,不改变原字符串,因此都作用在 &str 上.
|
find 返回的是字节索引
与上一篇呼应:find 给出的位置是字节索引,可直接用于切片.但若字符串含多字节字符,这个索引不等于"第几个字符".需要字符位置时,仍要配合 char_indices() 自行换算.
替换,分割与清理
|
注意 split 返回的子串是借用原字符串的 &str,不复制数据;而 replace 和 to_uppercase 必须产生新内容,因此返回 String.这种"能借用就借用,必须新建才分配"的取舍贯穿整个字符串 API.
split 与连续分隔符
对 "a,b,,c" 用 split(',') 会得到一个空串元素 "".若想跳过空段,可用 split_terminator 或在迭代时 filter(|s| !s.is_empty()).这类细节在解析 CSV,路径等场景中很容易踩坑.
format! 与格式化语法
format! 是构造复杂字符串的主力.它和 println!,write! 共享同一套格式化语法,值得系统掌握.
|
格式化占位符里的 {} 像一张"排版要求单":冒号左边写"用哪个参数",右边写"怎么排版"--补零,对齐,进制,精度,都是排版指令.
Display 与 Debug:两种"变成文本"
为什么有的类型能用 {} 打印,有的却必须用 {:?}?这背后是两个不同的特征.
| 特征 | 占位符 | 面向对象 | 能否自动派生 |
|---|---|---|---|
Display |
{} |
终端用户,要求"优雅可读" | 否,必须手写 |
Debug |
{:?} |
开发者,用于调试 | 是,#[derive(Debug)] |
|
而 Debug 几乎总是用派生宏自动生成,无需手写:
|
为什么 Display 不能自动派生
因为"对人友好的展示形式"没有唯一标准答案:一个 Point 应该显示成 (1, 2) 还是 x=1 y=2?编译器无从猜测,只能由开发者定义.Debug 则有公认的"结构体字段一字排开"的默认格式,所以能派生.
一个经验法则:给开发者看用 Debug,给用户看用 Display.
parse:从字符串解析出类型
format! 把类型变成字符串,parse 则是反方向--把字符串解析成目标类型.它返回 Result,因为解析可能失败.
|
parse 靠类型标注推断目标
parse 怎么知道要解析成 i32 还是 f64?答案是靠类型推断:要么写出变量类型标注 let n: i32,要么用 turbofish 语法 parse::<f64>().任何实现了 FromStr 特征的类型都能被 parse 出来--这与下一篇要讲的类型转换体系直接相连.
快速回顾
- 拼接看代价:
push_str原地修改,+消耗左操作数,format!不夺所有权但有分配 - 只读操作返回
&str,新建操作返回String,区分能否零拷贝 - find 返回字节索引,split 子串借用原数据
- format! 语法:冒号左选参数,右定排版(补零,对齐,进制,精度)
- Display 手写给用户,Debug 派生给开发者
- parse 是 format 的逆向,返回
Result,靠类型标注确定目标
动手练习
- 实现 Display:给一个
struct Temperature { celsius: f64 }实现Display,让println!("{t}")输出形如25.0°C,再加#[derive(Debug)]对比两种打印效果. - split + parse:写一个函数解析
"1,2,3,4"这样的字符串为Vec<i32>,用split+parse实现,并妥善处理解析失败的情形(返回Result). - 格式化表格:用
format!把数字255同时输出为十进制,十六进制,二进制三种形式,宽度对齐成一张表格.