% Rust的字符串处理方式

字符串的处理方式对任何编程语言来说都是很重要的。如果你具有应用编程语言背景,会对系统编程语言在字符串处理上的复杂性感到吃惊,尤其是在变长字符串结构的高效的处理和动态内存申请方面将涉及很多操作系统内部的细节。幸运的是,Rust这门编程语言有很多便捷的方式帮助我们进行处理。

在Rust里,字符串是一个连续的UTF-8编码的Unicode字节流,并且字符串不包含空节点和空终止符,每个字符串使用指针进行引用。

Rust有两个字符串类型:&strString

&str

&str称为字符串切片,字符串的字面量就是&str格式。

  1. let string = "hello there.";

同其它Rust的类型一样,字符串切片也有lifetime,一个字符串常量就是&'static str。字符串切片在作为函数参数的时候,可以没有明确的lifetime,因为这时可以自动推断:

  1. fn takes_slice(slice: &str) {
  2. println!("Got: {}", slice);
  3. }

同容器切片一样,字符串切片可以简单认为是已经存在的字符串,就像&'static strString的实例。

&str主要是在栈上生成:

  1. use std::str;
  2. let x: &[u8] = &[b'a', b'b'];
  3. let stack_str: &str = str::from_utf8(x).unwrap();

String

String定义了具有owned所有权的,并且可以动态生长的字符串类型,也是UTF-8编码。String主要是在堆上生成。

  1. let mut s = "Hello".to_string();
  2. println!("{}", s);
  3. s.push_str(", world.");
  4. println!("{}", s);

你可以使用as_slice()方法将一个String对象强制转换为&str类型:

  1. fn takes_slice(slice: &str) {
  2. printn!("Got: {}", slice);
  3. }
  4. fn main() {
  5. let s = "Hello".to_string();
  6. takes_slice(s.as_slice());
  7. }

最佳实践

String vs. &str

通常String用于对字符串的owned&str用于对字符串的borrowed。这同Vec<T>&[T]T&T的关系。所以,一般情况下使用&str,而不使用String,使用String会增加lifetime的复杂性。

泛型

写一个泛型的字符串,一般使用&str

  1. fn some_string_length(x: &str) -> unit {
  2. x.len()
  3. }
  4. fn main() {
  5. let s = "Hello, world";
  6. println!("{}", some_string_length(s)); // 12
  7. let s = "Hello, world".to_string();
  8. println!("{}", some_string_length(s.as_slice())); //12
  9. }

比较

比较String与字符串常量,一般使用as_slice()方法进行类型转换。

  1. fn compare(x: String) {
  2. if x.as_slice() == "Hello" {
  3. println!("yes");
  4. }
  5. }

这是因为将String转换为&str类型比较容易,反之将&str转换为String涉及到堆内存分配的问题。

字符串的索引

你可能会认为获得字符串的某个字符成员可以这样做:

  1. let s = "hello".to_string();
  2. println!("{}", s[0]);

这种做法在Rust中是错误的,这是因为字符串的每个成员可能会占据不同的字节。

我们知道Unicode有3个基本的组成方式:

  • 编码单元,用于存储基本数据类型
  • 代码指针/unicode标量值
  • 字母

一般使用graphemes()方法来获取&str的成员:

  1. let s = "u͔n͈̰̎i̙̮͚̦c͚̉o̼̩̰͗d͔̆̓ͥé";
  2. for i in s.graphemes(true) {
  3. println!("{}", i);
  4. }

打印结果:

  1. u͔
  2. n͈̰̎
  3. i̙̮͚̦
  4. c͚̉
  5. o̼̩̰͗
  6. d͔̆̓ͥ
  7. é

注意:i在这里是&str类型,一个字母由多个码点组成,所以char(单个字符)的概念在这里不适合。

如果你想打印每个字母单独的码点,可以使用.char()方法:

  1. let s = "u͔n͈̰̎i̙̮͚̦c͚̉o̼̩̰͗d͔̆̓ͥé";
  2. for i in s.chars() {
  3. println!("{}", i);
  4. }

打印结果:

  1. u
  2. ͔
  3. n
  4. ̎
  5. ͈
  6. ̰
  7. i
  8. ̙
  9. ̮
  10. ͚
  11. ̦
  12. c
  13. ̉
  14. ͚
  15. o
  16. ͗
  17. ̼
  18. ̩
  19. ̰
  20. d
  21. ̆
  22. ̓
  23. ͥ
  24. ͔
  25. e
  26. ́

如果你想打印每个字节的码点,可以使用.bytes()方法:

  1. let s = "u͔n͈̰̎i̙̮͚̦c͚̉o̼̩̰͗d͔̆̓ͥé";
  2. for i in s.bytes() {
  3. println!("{}", i);
  4. }

打印结果:

  1. 117
  2. 205
  3. 148
  4. 110
  5. 204
  6. 142
  7. 205
  8. 136
  9. 204
  10. 176
  11. 105
  12. 204
  13. 153
  14. 204
  15. 174
  16. 205
  17. 154
  18. 204
  19. 166
  20. 99
  21. 204
  22. 137
  23. 205
  24. 154
  25. 111
  26. 205
  27. 151
  28. 204
  29. 188
  30. 204
  31. 169
  32. 204
  33. 176
  34. 100
  35. 204
  36. 134
  37. 205
  38. 131
  39. 205
  40. 165
  41. 205
  42. 148
  43. 101
  44. 204
  45. 129

其它文档