在 Rust 中拼接字符串:新手常犯的 5 大错误(以及如何避免)

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

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

开始课程

引言:为什么在 Rust 中不能简单地拼接字符串?

\n\n

如果您是从 Python 或 JavaScript 转到 Rust,那么最让您震惊的,很可能就是尝试编写 "Hello, " + "world!" 时。编译器会用红色波浪线和一条神秘的错误消息迎接您。在 Python 的世界里,这样的操作会立即生效,但 Rust 是另一种“猛兽”。在 Stack Overflow 上,"How do I concatenate strings?" 这个问题获得了数十万次浏览,这并非偶然。

\n\n

问题在于,Rust 中的字符串拼接不仅仅是语法糖。它涉及到内存管理、所有权(ownership)和数据类型。如果您是新手,很容易踩到经典的“坑”:丢失字符串的所有权、意外复制千兆字节的数据,或者只是混淆了 String 和 &str。

\n\n

在本文中,我们将分析新手在拼接 Rust 字符串时最常犯的 5 个错误,并展示如何避免它们。您将了解何时使用 +,何时使用 format!,以及何时使用 push_str。让我们开始吧!

\n\n

错误 #1:忘记所有权(ownership)的转移

\n\n

表现形式

\n\n

假设您编写了以下代码:

\n\n
let s1 = String::from("Привет");\nlet s2 = String::from(", мир!");\nlet s3 = s1 + &s2; // 错误?还是没有?\nprintln!("{}", s1); // 编译错误!
\n\n

您期望 s1 仍然可用,但编译器却说:“不,s1 已经被移动(moved)了”。

\n\n

错误的本质

\n\n

在 Rust 中,字符串的 + 运算符使用签名 fn add(self, s: &str) -> String。请注意 self —— 这意味着第一个字符串被消耗(consumes),您不能再使用它。您实际上是把 s1 “交给”了 add 函数,它变得不可用。

\n\n

如何修复

\n\n

有两种方法:

\n\n
    \n
  • 克隆字符串(如果您需要原始字符串):let s3 = s1.clone() + &s2;
  • \n
  • 改变顺序或使用 format!:
  • \n
\n\n
let s1 = String::from("Привет");\nlet s2 = String::from(", мир!");\nlet s3 = format!("{}{}", s1, s2); // s1 和 s2 仍然可用\nprintln!("{}", s1); // 正常工作!
\n\n

建议:如果您不打算在拼接后使用 s1,请放心使用 +。它比 format! 更快,因为不需要从头创建新的缓冲区。

\n\n

错误 #2:混淆 String 和 &str

\n\n

表现形式

\n\n

您尝试将 String 与字面量相加:

\n\n
let s = String::from("Hello");\nlet result = s + " world"; // 正常工作!
\n\n

但如果您反过来尝试:

\n\n
let s = "Hello";\nlet result = s + String::from(" world"); // 错误!
\n\n

错误的本质

\n\n

+ 运算符仅在左侧是 String 且右侧是 &str 时有效。字面量 " world" 自动是 &str 类型,因此第一个示例有效。在第二个示例中,s 是 &str 类型,它没有 add 方法。

\n\n

如何修复

\n\n

将 &str 转换为 String 或使用 format!:

\n\n
let s = "Hello";\nlet result = format!("{} {}", s, "world"); // 通用方法\n// 或者\nlet result = s.to_string() + " world"; // to_string() 创建 String
\n\n

黄金法则:String 是拥有类型(owned),&str 是借用类型(borrow)。拼接操作始终要求左侧拥有所有权。

\n\n

错误 #3

博客

书籍推荐