如何理解Rust中的字符串(一)

188 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第15天,点击查看活动详情

rust中的字符串比较特别,在Rust的核心语言层面,只有一个字符串类型:字符串切片str(或&str),而String类型是来自于Rust标准库中的类型。

Rust中的字符串是什么?

  • byte的集合
  • 并且它提供了一些方法,这些方法能将byte解析为文本

在Rust的核心语言层面,只有一个字符串类型:字符串切片str(或&str)

字符串切片:对存储在其他地方、utf-8编码的字符串的引用

  • 字符串字面值:存储在二进制文件中,也是字符串切片

切片slice(&str)

Rust还提供了另外一种不持有所有权的类型:切片(slice)

假如我们有这样一个需求,编写一个函数:

  • 它接收字符串作为参数
  • 返回它在这个字符串找到的第一个单词
  • 如果函数没有找到空格,返回整个字符串

如果直接实现这个需求比较繁琐,但我们可以使用字符串切片来简单的进行编写代码

字符串切片是指向字符串中一部分内容的引用:

let s = String::from("hello world");
let hello = &s[0..5];
let world = &s[6..11];
println!("{},{}", hello, world); //hello,world
  • 开始索引:起始位置的索引
  • 结束索引:终止位置的下一个索引

关于切片的语法糖:

  • 如果开始索引为0,可省略
  • 如果结束索引为字符串长度,可省略
let s = String::from("hello world");
let hello = &s[..5];
let world = &s[6..];
let whole = &s[..];// &s[0..s.len()]
println!("{},{}", hello, world); //hello,world

注意:

  • 字符串切片的范围索引必须发生在有效的UTF-8边界内
  • 如果尝试从一个多字节的字符串中创建字符串切片,程序将会报错并推出

下面我们将实现之前提到的需求:

fn main() {
    let s = String::from("yes hello world");
    let first_w = first_world(&s);
    println!("{}", first_w); //yes
}

fn first_world(s: &String) -> &str {
    let bytes = s.as_bytes();
    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[0..i];
        }
    }
    &s[..]
}

字符串字面值是切片

字符串字面值被直接存储在二进制文件中

let s = "nice demo"; //s:&str

变量s的类型是&str,它是一个指向二进制程序特定位置的切片

  • &str是不可变引用,所以字符串的字面值是不可变的

将字符串切片作为参数传递

有经验的开发者会采用&str作为参数类型,因为这样就可以同时接收String和&str类型的参数了:

fn first_word(s:&str)->&str{}
  • 使用字符串切片,直接调用该函数
  • 使用String,可以创建一个完整的String切片来调用该函数

定义函数时使用字符串切片来代替字符串引用会使我们的API更加通用,且不会损失任何功能:

fn main() {
    let s = String::from("yes hello world");
    let first_w = first_world(&s[..]);

    let s1 = "hello demo";
    let first_w1 = first_world(s1);
}

fn first_world(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 a = [1, 2, 3, 4, 5];
    let slice = &a[1..3];//slice: &[i32]
    println!("{:?}", slice); //[2, 3]
}