如何将两个字符串连接成第三个字符串,而不需要拷贝?

3

我是Rust的新手,目前正在阅读《Rust编程语言》这本书。

我对这个示例很感兴趣:

fn main() {
    let s1 = String::from("Hello, ");
    let s2 = String::from("world!");
    let s3 = s1 + &s2; // note s1 has been moved here and can no longer be used
}

是否可能不仅占有s1,还占有s2,以便使s2无效化,就像s1一样,使s3成为唯一可能可用的变量?


为什么?听起来像是一个X/Y问题。 - mcarton
@mcarton,我其实更好奇的是是否可以创建一个字符串而不显式复制其中一个字符串的内容。基本上,新字符串应该包含两个类型为String的指针依次排列。现在我意识到这应该是一种自定义类型,无法使用默认的String实现来完成。这更像是一个理论问题,要求语言特定的设计,这对我来说还不太熟悉。 - cr7pt0pl4gu3
@cr7pt0pl4gu3 不行,为了连接这些字符串,需要有一个单一的连续分配。但即使它们在物理内存中相邻(这不太可能),s1和s2也是不同的分配。因此,在标准库中,你能做的最好的事情就是将s2附加到s1的缓冲区末尾。 - Masklinn
1
尽管标准库之外可能存在一个ropes库:ropes是伪字符串,设计为缓冲区的浅树,其主要目标是轻松地原地更新文本段,而无需移动大量数据。但副作用是绳索通过引用两个字符串来连接它们。 - Masklinn
1个回答

5

更新的答案

这是不可能的。一个 String 必须是在内存中单个连续的分配块。如果你想自己实现一个简单的解决方案,可以定义一个类似于以下类型:

struct MyString {
    parts: Vec<String>,
}

impl MyString {
    fn concat(&mut self, other: String) {
        self.parts.push(other);
    }
}

然而,重新实现自定义类型的每个有用的`String`方法将变得繁琐且容易出错。您可以找到一个实现了Rope数据结构的Rust crate,an-rope 似乎就是这样一个 Crate,但它只支持一小部分`String`方法。

当我将 OP 的问题解释为关于无效化和移动变量时,我给出了此答案:

原始回答

通过将它传递到drop函数中,您可以将任何非Copy变量无效化并移动到其他地方:

fn main() {
    let s1 = String::from("Hello, ");
    let s2 = String::from("world!");
    let s3 = s1 + &s2; // s1 invalidated
    drop(s2); // s2 invalidated
    // now only s3 is usable
}

如果这是您的应用程序中常见的模式,您可以编写一个函数,该函数接管2个字符串的所有权并返回连接结果。
fn concat(s1: String, s2: String) -> String {
    s1 + &s2
}

fn main() {
    let s1 = String::from("Hello, ");
    let s2 = String::from("world!");
    let s3 = concat(s1, s2); // s1 & s2 invalidated
    // now only s3 is usable
}

@cr7pt0pl4gu3 请注意,这并不能防止复制第二个“String”,因此禁用它的所有权有点毫无意义。 - L. F.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接