基于Rust的Service Mesh:微服务如何变得更快速、更安全

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

在 Rust 中遍历字符串:底层原理揭秘



你是否曾好奇过,当你写下 for c in "Привет".chars() 时,实际发生了什么?如果你来自 Python 或 JavaScript 的世界,那里的字符串只是字符数组,那么 Rust 可能像一位严格的老师,让你在每次操作前都要思考。而这正是它的妙处!



在 Stack Overflow 上,"如何按字符遍历字符串" 这个问题已经积累了数千次浏览量。这并非偶然。在 Rust 中,没有简单的 string[i] —— 而且这是有充分理由的。让我们来弄清楚如何正确、高效且无意外地遍历字符串。



为什么 Rust 不允许直接通过索引获取字符?



这一切都源于 UTF-8 编码。与许多语言不同,Rust 使用 UTF-8 而非 ASCII 或 UCS-2 来存储字符串。这意味着一个字符可能占用 1 到 4 个字节。例如:



  • 'a' — 1 个字节
  • 'é' — 2 个字节
  • '中' — 3 个字节
  • '😊' — 4 个字节


如果 Rust 允许 string[0],那么 "😊"[0] 返回的将不是这个表情符号,而是它字节表示的一半——即乱码。因此,Rust 在编译器层面就保护你免受此类错误的影响。



遍历字符串的主要方式



Rust 提供了三种主要的迭代方法,每种方法都有其特定用途。选择哪种取决于你的具体需求:是需要字符、字节,还是字素簇。



1. 按字符 (char) 遍历



最常用的方式是 .chars() 方法。它返回一个用于迭代 Unicode 字符(类型为 char)的迭代器。



fn main() {    let text = "Привет, мир! 🌍";        for c in text.chars() {        println!("{}", c);    }        // 输出:    // П    // р    // и    // в    // е    // т    // ,    // (空格)    // м    // и    // р    // !    // (空格)    // 🌍}


在底层,.chars() 会解析 UTF-8 字节序列,并返回每个有效的字符。如果字符串包含无效字节,迭代器可能会返回 std::char::REPLACEMENT_CHARACTER ('�')。



重要提示:.chars() 的时间复杂度是 O(n)。迭代器会遍历所有字节,同时进行解码。对于短字符串,这个过程是瞬时的;但对于长字符串(数兆字节的文本),其开销可能会变得显著。



2. 按字节 (bytes) 遍历



如果你需要原始字节,请使用 .bytes()。这是一种快速的方法,但你得到的是字节,而非字符。



fn main() {    let text = "Hello";        for byte in text.bytes() {        println!("{}", byte);    }        // 输出:    // 72 (H)    // 101 (e)    // 108 (l)    // 108 (l)    // 111 (o)}


对于英文文本,这等同于按字符遍历。但对于俄语或表情符号,则并非如此。例如,"Привет" 在字节表示中占用 12 个字节(每个字符 2 个字节),而 "🌍" 占用 4 个字节。



使用场景: 当你处理二进制数据、协议,或者希望在不进行 UTF-8 解码的情况下获得最大性能时。



3. 按字素簇遍历



这一点更有趣。有时,一个视觉上的"字符"由多个 Unicode 字符组成。例如,国家旗帜或带有变音符号的字母:"é" 既可以表示为单个字符 é (U+00E9),也可以表示为 e + ◌́ (U+0065 + U+0301) 的组合。



要处理字素簇,你需要添加 unicode-segmentation crate:



// Cargo.toml// [dependencies]// unicode-segmentation = "1.10"

use unicode_segmentation::UnicodeSegmentation;

fn main() { let text = "é"; // chars() 会返回 1 个字符

博客

书籍推荐