字符串的[]运算符,与向量的切片链接

x4r*_*rkz 0 string vector rust

当你执行s [n],其中s是一个字符串时,为什么你必须遍历字符串才能找到字符串的n字母.(根据https://doc.rust-lang.org/book/strings.html)

根据我的理解,字符串是一个字符数组,一个字符串是一个4字节或4字节的数组.所以越来越第n个信会像这样类似:v [4*n..4*N + 4],其中v是一个向量?

v [i..j]的费用是多少?

我假设v [i..j]的成本是ji,因此s [n]的成本应该是4.

E_n*_*ate 6

注意:第二版The Rust Programming Language对Rust中的Strings进行了改进和流畅的解释,您可能也希望阅读它.下面的答案虽然仍然准确,但引用了本书的第一版.


我将尝试通过引用本书(https://doc.rust-lang.org/book/strings.html)来澄清这些关于Rust中字符串的误解.

"字符串"是一系列Unicode标量值,编码为UTF-8字节流.保证所有字符串都是UTF-8序列的有效编码.

考虑到这一点,加上UTF-8代码点可变大小(取决于字符1至4字节),在锈的所有字符串,无论它们是&strString,不是字符数组,并且不能像这样处理.它进一步解释了为什么切片:

因为字符串是有效的UTF-8,所以它们不支持索引:

let s = "hello";

println!("The first letter of s is {}", s[0]); // ERROR!!!
Run Code Online (Sandbox Code Playgroud)

通常,使用[]访问矢量非常快.但是,因为UTF-8编码字符串中的每个字符都可以是多个字节,所以必须遍历字符串才能找到字符串的nᵗʰ字母.这是一项非常昂贵的操作,我们不想误导.

与问题中提到的不同,人们做不到s[n],因为虽然理论上这将允许我们在恒定时间内获取第n个字节,但该字节不能保证自己有意义.

v [i..j]的费用是多少?

切片的成本实际上是不变的,因为它是在字节级完成的:

您可以使用切片语法获取切片:

let dog = "hachiko";
let hachi = &dog[0..5];
Run Code Online (Sandbox Code Playgroud)

但请注意,这些是字节偏移,而不是字符偏移.所以这会在运行时失败:

let dog = "?????";
let hachi = &dog[0..2];
Run Code Online (Sandbox Code Playgroud)

有这个错误:

线程''恐慌'索引0和/或2 ?????不在字符边界上'

基本上,切片是可以接受的,并且将产生该字符串的新视图,因此不会制作副本.但是,只有当您完全确定偏移在字符边界方面是正确的时,才应该使用它.

为了迭代字符串的每个字符,您可以改为chars():

let c = s.chars().nth(n);
Run Code Online (Sandbox Code Playgroud)

即使考虑到这一点,请注意,如果您希望处理UTF-8中的字符修饰符(它们本身是标量值但不应单独处理),处理Unicode字符可能并不完全符合您的要求.现在从strAPI中引用:

fn chars(&self) -> Chars

返回字符串切片的字符上的迭代器.

由于字符串切片由有效的UTF-8组成,我们可以通过char迭代字符串切片.此方法返回此类迭代器.

重要的是要记住char表示Unicode标量值,并且可能与您对"字符"的概念不符.对字形集群的迭代可能是你真正想要的.

请记住,字符可能与人类对人物的直觉不符:

let y = "y?";

let mut chars = y.chars();

assert_eq!(Some('y'), chars.next()); // not 'y?'
assert_eq!(Some('\u{0306}'), chars.next());
assert_eq!(None, chars.next());
Run Code Online (Sandbox Code Playgroud)

所述unicode_segmentation板条箱提供了一种手段,以限定字形群集边界:

extern crate unicode_segmentation;

use unicode_segmentation::UnicodeSegmentation;

let s = "a?e?o??\r\n";
let g = UnicodeSegmentation::graphemes(s, true).collect::<Vec<&str>>();
let b: &[_] = &["a?", "e?", "o??", "\r\n"];
assert_eq!(g, b);
Run Code Online (Sandbox Code Playgroud)

  • UTF-8是Unicode码点到字节的可变长度编码.每个代码点最终可以为1,2,3或4个字节.碰巧ASCII字符(主要用于英语)最终为1字节(与ASCII的值相同). (3认同)