rust:第六章slice类型
目录
第六章 Slice类型
6.1 什么是Slice
Slice(切片)允许你引用集合中一段连续的元素序列,而不用引用整个集合。Slice是一种动态大小类型(DST, Dynamically Sized Type)。
Slice的特点
- 没有所有权
- 是对原数据的引用
- 长度在运行时确定
- 语法:[T] 或 &[T]
6.2 字符串Slice
创建字符串Slice
字符串Slice(&str)是String的一部分值的引用:
let s = String::from("hello world"); let hello = &s[0..5]; // 从索引0到5(不包括5) let world = &s[6..11]; // 从索引6到11(不包括11) println!("{} {}", hello, world); // "hello world"
图示:
s: String +--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+ | h | e | l | l | o | | w | o | r | l | d | \0 | +--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+ 0 1 2 3 4 5 6 7 8 9 10 11 hello = &s[0..5] +--------+--------+--------+--------+--------+ | h | e | l | l | o | +--------+--------+--------+--------+--------+ world = &s[6..11] +--------+--------+--------+--------+--------+ | w | o | r | l | d | +--------+--------+--------+--------+--------+
Slice语法
let s = String::from("hello"); let slice = &s[0..2]; // 从开始到2(不包括2) let slice = &s[..2]; // 同上,省略开始索引 let slice = &s[3..len]; // 从3到结尾 let slice = &s[3..]; // 同上,省略结束索引 let slice = &s[0..len]; // 整个字符串 let slice = &s[..]; // 同上,两者都省略
字符串Slice的边界
必须在有效的UTF-8字符边界:
let s = "中国人"; // 每个汉字3字节 let a = &s[0..3]; // "中" - 正确 // let b = &s[0..2]; // 错误!不是有效的UTF-8边界
运行时的越界会导致panic:
let s = String::from("hello world"); // let slice = &s[0..100]; // panic!越界
6.3 字符串字面量就是Slice
字符串字面量的类型是&str,它是一个指向二进制程序特定位置的slice:
let s = "Hello, world!"; // s的类型是&str
这也是为什么字符串字面量是不可变的:&str是不可变引用。
&str vs String
| 特性 | String | &str |
| —— | ——– | —— |
| 所有权 | 有 | 无(借用) |
| 存储位置 | 堆 | 任意(堆/栈/静态数据区) |
| 可变性 | 可变(mut) | 总是不可变 |
| 大小 | 运行时确定 | 编译时确定指针+长度 |
6.4 将String转为&str
使用&操作符
let s = String::from("hello"); let slice: &str = &s;
使用as_str()
let s = String::from("hello"); let slice = s.as_str();
使用Deref自动转换
fn takes_slice(s: &str) { println!("{}", s); } fn main() { let s = String::from("hello"); takes_slice(&s); // 自动转换 }
6.5 字符串Slice方法
常用方法
let s = "hello world"; // 长度(字节数) println!("长度:{}", s.len()); // 是否为空 println!("是否为空:{}", s.is_empty()); // 获取字符 for c in s.chars() { println!("{}", c); } // 分割 let parts: Vec<&str> = s.split(' ').collect(); println!("{:?}", parts); // 包含检查 println!("包含'world':{}", s.contains("world")); // 替换 let new_s = s.replace("world", "Rust"); println!("{}", new_s); // 去除空白 let s2 = " hello ".trim(); println!("'{}'", s2);
6.6 数组Slice
创建数组Slice
let a = [1, 2, 3, 4, 5]; let slice = &a[1..3]; // &[i32] 包含 [2, 3] assert_eq!(slice, &[2, 3]);
数组Slice的方法
let a = [1, 2, 3, 4, 5]; let slice = &a[1..4]; // 长度 println!("长度:{}", slice.len()); // 是否为空 println!("是否为空:{}", slice.is_empty()); // 获取元素 println!("第一个元素:{}", slice[0]); println!("最后一个元素:{}", slice.last().unwrap()); // 遍历 for item in slice.iter() { println!("{}", item); } // 查找 if let Some(pos) = slice.iter().position(|&x| x == 3) { println!("找到3在索引{}", pos); }
6.7 Slice的内存布局
Slice由两个部分组成:
- 指向数据的指针
- 长度(usize)
在64位系统上,&str和&[T]都是16字节(两个usize)。
use std::mem; fn main() { println!("&str大小:{}", mem::size_of::<&str>()); println!("&[i32]大小:{}", mem::size_of::<&[i32]>()); // 64位系统输出:16 }
6.8 使用Slice编写函数
第一个单词提取
fn first_word(s: &str) -> &str { let bytes = s.as_bytes(); for (i, &item) in bytes.iter().enumerate() { if item == b' ' { return &s[0..i]; } } &s[..] } fn main() { let my_string = String::from("hello world"); let word = first_word(&my_string); println!("第一个单词是:{}", word); let my_string_literal = "hello world"; let word = first_word(my_string_literal); println!("第一个单词是:{}", word); }
使用&str作为参数的优势:
- 可以接受String的引用
- 也可以接受字符串字面量
- 更灵活、更通用
泛型Slice函数
fn largest<T: PartialOrd>(slice: &[T]) -> Option<&T> { if slice.is_empty() { return None; } let mut largest = &slice[0]; for item in slice.iter() { if item > largest { largest = item; } } Some(largest) } fn main() { let numbers = [34, 50, 25, 100, 65]; match largest(&numbers) { Some(max) => println!("最大值:{}", max), None => println!("数组为空"), } }
6.9 其他类型的Slice
Vec的Slice
let v = vec![1, 2, 3, 4, 5]; let slice = &v[1..3]; println!("{:?}", slice); // [2, 3]
可变Slice
fn clear_slice(slice: &mut [i32]) { for item in slice.iter_mut() { *item = 0; } } fn main() { let mut arr = [1, 2, 3, 4, 5]; clear_slice(&mut arr[1..4]); println!("{:?}", arr); // [1, 0, 0, 0, 5] }
6.10 Slice与迭代器
iter()方法
let arr = [1, 2, 3, 4, 5]; // 不可变迭代 for item in arr.iter() { println!("{}", item); } // 使用iter() for item in arr[..].iter() { println!("{}", item); }
windows()和chunks()
let arr = [1, 2, 3, 4, 5]; // 滑动窗口 for window in arr.windows(3) { println!("{:?}", window); } // 输出:[1, 2, 3], [2, 3, 4], [3, 4, 5] // 分块 for chunk in arr.chunks(2) { println!("{:?}", chunk); } // 输出:[1, 2], [3, 4], [5]
练习题
练习题6.1:字符串反转
fn reverse_words(s: &str) -> String { s.split_whitespace() .rev() .collect::<Vec<_>>() .join(" ") } fn main() { let s = "hello world from Rust"; println!("原字符串:{}", s); println!("反转单词:{}", reverse_words(s)); }
练习题6.2:查找子串
fn find_substring(s: &str, sub: &str) -> Option<usize> { s.find(sub) } fn main() { let s = "hello world"; match find_substring(s, "world") { Some(index) => println!("找到子串在索引{}", index), None => println!("未找到子串"), } }
练习题6.3:旋转数组
fn rotate_left<T>(slice: &mut [T], k: usize) { slice.rotate_left(k); } fn main() { let mut arr = [1, 2, 3, 4, 5]; println!("原数组:{:?}", arr); rotate_left(&mut arr, 2); println!("左移2位后:{:?}", arr); // [3, 4, 5, 1, 2] }
练习题6.4:字符串分析
fn analyze_string(s: &str) { println!("字符串:{}", s); println!("字符数(Unicode):{}", s.chars().count()); println!("字节数:{}", s.len()); println!("单词数:{}", s.split_whitespace().count()); println!("行数:{}", s.lines().count()); } fn main() { let text = "Hello, Rust!\n这是第二行。\n这是第三行。"; analyze_string(text); }
练习题6.5:实现trim功能
fn my_trim(s: &str) -> &str { let bytes = s.as_bytes(); // 找到第一个非空白字符 let start = bytes.iter() .position(|&b| !b.is_ascii_whitespace()) .unwrap_or(bytes.len()); // 找到最后一个非空白字符 let end = bytes.iter() .rposition(|&b| !b.is_ascii_whitespace()) .map(|i| i + 1) .unwrap_or(0); &s[start..end] } fn main() { let s = " hello world "; println!("'{}'", my_trim(s)); }
本章小结
本章详细介绍了Rust的Slice类型:
- 字符串Slice(&str):String的一部分或字符串字面量
- 数组Slice(&[T]):数组或向量的一部分
- Slice语法:[start..end],可以省略start或end
- UTF-8边界:字符串Slice必须在有效的UTF-8字符边界
- 内存布局:指针+长度,16字节(64位系统)
- 函数参数:使用&str比&String更灵活
Slice是Rust中处理序列数据的重要工具,理解Slice有助于编写高效、安全的代码。
rust/第六章slice类型.txt · 最后更改: 由 127.0.0.1
