切片应用
> 掌握 String 与 &str 的类型转换方法,学习数组切片和切片作为函数参数的最佳实践。
类型转换
String → &str
rust
▶ Runfn main() {
let s = String::from("hello");
// 方法 1:隐式转换(Deref coercion)
let str1: &str = &s;
// 方法 2:显式方法
let str2: &str = s.as_str();
// 方法 3:使用&操作符
let str3 = &s[..];
println!("str1={}, str2={}, str3={}", str1, str2, str3);
}&str → String
rust
▶ Runfn main() {
let s: &str = "hello";
// 方法 1:to_string()
let string1: String = s.to_string();
// 方法 2:to_owned()
let string2: String = s.to_owned();
// 方法 3:into()
let string3: String = s.into();
// 方法 4:String::from
let string4: String = String::from(s);
println!("string1={}, string2={}", string1, string2);
}转换图
┌─────────────────────────────────────────────────────┐
│ String 和&str 转换关系 │
├─────────────────────────────────────────────────────┤
│ │
│ String ──────────────────→ &str │
│ • &s (隐式) • 零成本 │
│ • s.as_str() • 不复制数据 │
│ • &s[..] │
│ │
│ &str ────────────────────→ String │
│ • s.to_string() • 复制数据 │
│ • s.to_owned() • 堆分配 │
│ • s.into() │
│ • String::from(s) │
│ │
└─────────────────────────────────────────────────────┘函数参数使用 &str
最佳实践
rust
▶ Run// ✅ 推荐:接受 &str(最灵活)
fn greet(name: &str) {
println!("Hello, {}!", name);
}
fn main() {
let s1 = String::from("Alice");
let s2 = "Bob";
let s3 = String::from("Charlie");
// &str 可以接受多种类型
greet(&s1); // &String → &str
greet(s2); // &'static str
greet(&s3[..]); // 切片
greet(&s3[0..4]); // 部分切片
}不推荐的做法
rust
▶ Run// ❌ 不推荐:只接受 &String
fn greet_bad(name: &String) {
println!("Hello, {}!", name);
}
fn main() {
let s1 = String::from("Alice");
let s2 = "Bob";
greet_bad(&s1); // ✅ 可以
// greet_bad(s2); // ❌ 错误!&str 不能转&String
}为什么&str 更好?
&str 的通用性:
┌───────────────┐
│ 函数参数 │
│ fn(&str) │
└───────┬───────┘
│
┌──────────────┼──────────────┐
│ │ │
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ &String │ │ &str │ │ 切片 │
│ 转换 │ │ 直接 │ │ 直接 │
└─────────┘ └─────────┘ └─────────┘
结论:&str 是字符串参数的"通用接口"数组切片
基本用法
rust
▶ Runfn main() {
let numbers = [1, 2, 3, 4, 5];
// 创建数组切片
let slice: &[i32] = &numbers[1..4]; // [2, 3, 4]
println!("原数组:{:?}", numbers);
println!("切片:{:?}", slice);
// 验证类型
println!("切片类型:{:?}", std::any::type_name_of_val(&slice));
// 输出:&[i32; 3] 或&[i32]
}切片操作
rust
▶ Runfn main() {
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 从头开始
let first_three = &numbers[..3];
println!("前三个:{:?}", first_three); // [1, 2, 3]
// 从索引开始到末尾
let from_five = &numbers[4..];
println!("从第五个开始:{:?}", from_five); // [5, 6, 7, 8, 9, 10]
// 中间部分
let middle = &numbers[3..7];
println!("中间部分:{:?}", middle); // [4, 5, 6, 7]
// 整个数组
let all = &numbers[..];
println!("整个数组:{:?}", all);
// 单个元素(仍然是切片)
let single = &numbers[4..5];
println!("单个元素切片:{:?}", single); // [5]
// 空切片
let empty: &[i32] = &numbers[5..5];
println!("空切片:{:?}, 长度:{}", empty, empty.len());
}可变切片
rust
▶ Runfn main() {
let mut numbers = [1, 2, 3, 4, 5];
// 创建可变切片
let slice: &mut [i32] = &mut numbers[1..4];
// 修改切片
slice[0] = 10; // 修改 numbers[1]
slice[1] = 20; // 修改 numbers[2]
slice[2] = 30; // 修改 numbers[3]
println!("修改后:{:?}", numbers); // [1, 10, 20, 30, 5]
}切片作为函数参数
接受切片参数
rust
▶ Run// 接受切片
fn print_slice(slice: &[i32]) {
println!("切片:{:?}", slice);
}
fn main() {
let array = [1, 2, 3, 4, 5];
let vec = vec![1, 2, 3, 4, 5];
// 可以传递数组的切片
print_slice(&array);
print_slice(&array[1..4]);
// 可以传递 Vec 的切片
print_slice(&vec);
print_slice(&vec[1..4]);
}通用函数示例
rust
▶ Run// 计算切片的和
fn sum_slice(numbers: &[i32]) -> i32 {
numbers.iter().sum()
}
// 查找最大值
fn find_max(numbers: &[i32]) -> Option<i32> {
numbers.iter().max().copied()
}
// 打印格式化输出
fn print_formatted(title: &str, numbers: &[i32]) {
println!("=== {} ===", title);
println!("元素:{:?}", numbers);
println!("长度:{}", numbers.len());
println!("总和:{}", sum_slice(numbers));
println!("最大值:{:?}", find_max(numbers));
println!();
}
fn main() {
let nums1 = [1, 2, 3, 4, 5];
let nums2 = vec![10, 20, 30, 40, 50];
print_formatted("数组 1", &nums1);
print_formatted("Vec 2", &nums2);
print_formatted("部分切片", &nums1[1..4]);
}小结
String → &str:&s、s.as_str()、&s[..](零成本)&str → String:s.to_string()、String::from(s)(需要堆分配)- 函数参数优先使用
&str,可接受 String、字面量、切片 - 数组切片
&[T]可用于数组、Vec 的部分引用
练习题
详见:练习题