面试经典题:取字符串里的第一个单词。Java 里 split(" ")0 一行搞定,Rust 里却有个更优雅的答案------不复制、不修改、纯"划重点"。但随手一写就可能踩进 UTF-8 的坑,直接 panic。今天把切片一次学透。
一、是什么:切片 = 划重点,不复印原文
昨天说 &str 是"书签",今天补上它的完整身份:切片(slice)是对一段连续数据的引用视图。它的核心动作就是你读书时干的------拿荧光笔划一段,书还是那本书,你只带走了"范围":
rust
let s = String::from("hello world");
let hello = &s[0..5]; // 划下 0 到 5 这段(不含 5)
let world = &s[6..11]; // 划下 6 到 11 这段
// 语法糖:&s[..5] 从头划,&s[6..] 划到尾,&s[..] 整本划
关键认知:切片不复制数据。hello 只是存了"指向 s 的指针 + 长度"两个信息(共 16 字节),"hello world" 那份数据在堆上一动不动。昨天留的悬念这里揭晓------&str全称就是"字符串切片",它本身就是切片大家族的成员。
而且切片不止能划字符串。数组、Vec 都能划,划出来的类型叫 &T:
rust
let scores = [90, 85, 77, 60, 95];
let top3: &[i32] = &scores[0..3]; // [90, 85, 77],同样零复制

二、为什么:为什么高手都爱切片?
三个理由,一个比一个实在:
-
零成本。把一段数据传给别人看,clone() 要整段复制堆内存;切片只传 16 字节的"范围票据"。数据越大,差距越明显。
-
边界编译期就锁定。切片带着"长度"信息,越界访问编译器直接拦(&s0..100 编译不报错但运行会 panic------但如果长度是编译期常量,编译器能提前发现)。比 C 语言"拿着裸指针随便指"安全一个时代。
-
让编译器替你抓 bug。来看切片的杀手锏------借用检查会顺带保护被切片的数据:
rust
fn main() {
let mut s = String::from("hello world");
let hello = &s[0..5]; // 划了重点
s.clear(); // ❌ 编译报错!书要被撕了,划的重点全成悬空纸屑
println!("{}!", hello);
}
clear() 要清空字符串,而 hello 还指着里面------如果放行,hello 瞬间变成悬空引用(指着一堆已释放的内存)。Day 9 预告的"悬垂引用"在这里第一次被编译器当场抓获,而抓住它的机制,就是切片的借用检查。

三、怎么办:实战"第一个单词"函数
先看错误示范------用字节下标硬切:
rust
fn first_word(s: &str) -> &str {
&s[..5] // ❌ 碰运气!换一句 "你好世界" 直接 panic
}
问题在哪?Rust 的字符串是 UTF-8 编码,一个汉字占 3 个字节。&s..2 想取第一个汉字,实际切在了汉字的"半个身子"上------Rust 不允许出现残缺字符,直接 panic:byte index 2 is not a char boundary。这是新手切字符串的最高频翻车点。
正确姿势:不数字节,找空格位置:
rust
fn first_word(s: &str) -> &str {
// find 返回 Option<usize>:找到空格返回下标,没找到返回 None
match s.find(' ') {
Some(i) => &s[..i], // 有空格:划到空格前
None => s, // 没空格:整个单词就是全句
}
}
fn main() {
println!("{}", first_word("hello world")); // hello
println!("{}", first_word("你好世界")); // 你好世界 ✅ 完整无 panic
println!("{}", first_word("Rust")); // Rust
}
零复制、不修改原串、中文安全------这就是"不出错的字符串处理函数"的完整答案。两个细节品味一下:
参数写 &str(昨天教的通吃写法,这里立刻用上了);
返回值也是 &str------返回的"书签"夹在原书里,生命周期问题明天(Day 12)细讲,今天先记住:返回切片 = 返回原文的一段视图。
find 返回的 Option 今天先当"可能失败的下标"用,Day 16 讲完 enum 再回头深挖。

✍️ 今日刻意练习
必做:写 second_word(s: &str) -> &str------返回第二个单词。提示:找第一个空格 i,再在 &si+1.. 里找第二个空格。写出后用 "a b c" 和 "中文 测试" 各测一次。
选做:故意写 let c = "你好"; &c..1 跑一次,把 panic 信息读完整------确认编译器说的 "not a char boundary" 是什么意思(延续我们"读完整报错"的老传统)。
📌 今日小结
切片 = 划重点
:只记"指针 + 范围",零复制;&str 是字符串切片,&T 是集合切片。
切片自带保护
:数据被改动时,借用检查当场拦截,悬空引用无处遁形。
切字符串别数字节
:UTF-8 下中文占 3 字节,硬切半个字必 panic;用 find 找边界才安全。
💬 切片是 Rust 递给你的一支荧光笔:划走的从来不是纸,而是你的注意力。你写 first_word 时踩到 char boundary 的坑了吗?评论区贴出你的 panic 信息,让大家看看谁摔得最标准。 🔜 明日预告:Day 12《生命周期初识:'a 到底是什么?》------那个吓退无数人的撇号,其实 90% 的代码根本不用写。