Skip to content

切片应用

> 掌握 String 与 &str 的类型转换方法,学习数组切片和切片作为函数参数的最佳实践。

类型转换

String → &str

rust
fn 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);
}
▶ Run

&str → String

rust
fn 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);
}
▶ Run

转换图

┌─────────────────────────────────────────────────────┐
│           String 和&str 转换关系                     │
├─────────────────────────────────────────────────────┤
│                                                     │
│   String ──────────────────→ &str                  │
│   • &s         (隐式)        • 零成本               │
│   • s.as_str()               • 不复制数据           │
│   • &s[..]                                           │
│                                                     │
│   &str ────────────────────→ String                │
│   • s.to_string()           • 复制数据              │
│   • s.to_owned()            • 堆分配                │
│   • s.into()                                          │
│   • String::from(s)                                  │
│                                                     │
└─────────────────────────────────────────────────────┘

函数参数使用 &str

最佳实践

rust
// ✅ 推荐:接受 &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]);     // 部分切片
}
▶ Run

不推荐的做法

rust
// ❌ 不推荐:只接受 &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
}
▶ Run

为什么&str 更好?

&str 的通用性:

                ┌───────────────┐
                │   函数参数    │
                │   fn(&str)    │
                └───────┬───────┘

         ┌──────────────┼──────────────┐
         │              │              │
         ▼              ▼              ▼
   ┌─────────┐   ┌─────────┐   ┌─────────┐
   │ &String │   │  &str   │   │  切片   │
   │  转换   │   │  直接   │   │  直接   │
   └─────────┘   └─────────┘   └─────────┘

结论:&str 是字符串参数的"通用接口"

数组切片

基本用法

rust
fn 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]
}
▶ Run

切片操作

rust
fn 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());
}
▶ Run

可变切片

rust
fn 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]
}
▶ Run

切片作为函数参数

接受切片参数

rust
// 接受切片
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]);
}
▶ Run

通用函数示例

rust
// 计算切片的和
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]);
}
▶ Run

小结

  • String → &str&ss.as_str()&s[..](零成本)
  • &str → Strings.to_string()String::from(s)(需要堆分配)
  • 函数参数优先使用 &str,可接受 String、字面量、切片
  • 数组切片 &[T] 可用于数组、Vec 的部分引用

练习题

详见:练习题