如何拼接字符串?

567
如何连接以下类型的组合:
  • strstr
  • Stringstr
  • StringString

50
请注意,str&str不同的类型,99% 的情况下,您只需要关心 &str。有其他问题详细说明它们之间的差异。 - Shepmaster
这个回答解决了你的问题吗?如何在Rust中连接静态字符串 - Nathan McMillan
9个回答

695
当你连接字符串时,你需要分配内存来存储结果。最简单的方法是使用String&str
fn main() {
    let mut owned_string: String = "hello ".to_owned();
    let borrowed_string: &str = "world";
    
    owned_string.push_str(borrowed_string);
    println!("{owned_string}");
}

在这里,我们有一个自有的字符串,可以进行改变。这是高效的,因为它潜在地允许我们重复使用内存分配。对于StringString也有类似的情况,因为&String可以被解引用为&str
fn main() {
    let mut owned_string: String = "hello ".to_owned();
    let another_owned_string: String = "world".to_owned();
    
    owned_string.push_str(&another_owned_string);
    println!("{owned_string}");
}

在此之后,another_owned_string 保持不变(注意没有 mut 修饰符)。还有另一种变体,它“消耗”了 String,但不需要它是可变的。这是一个实现了 Add trait 的变体,它将一个 String 作为左操作数,将一个 &str 作为右操作数:
fn main() {
    let owned_string: String = "hello ".to_owned();
    let borrowed_string: &str = "world";
    
    let new_owned_string = owned_string + borrowed_string;
    println!("{new_owned_string}");
}

请注意,在调用+后,owned_string将不再可访问。
如果我们想要生成一个新的字符串,同时保留原有的字符串不变,最简单的方法是使用format!函数。
fn main() {
    let borrowed_string: &str = "hello ";
    let another_borrowed_string: &str = "world";
    
    let together = format!("{borrowed_string}{another_borrowed_string}");
    println!("{}", together);
}

请注意,输入变量都是不可变的,所以我们知道它们不会被修改。如果我们想要对任意组合的字符串进行相同的操作,我们可以利用字符串也可以进行格式化的事实。
fn main() {
    let owned_string: String = "hello ".to_owned();
    let another_owned_string: String = "world".to_owned();
    
    let together = format!("{owned_string}{another_owned_string}");
    println!("{}", together);
}

你不必使用format!。你可以克隆一个字符串,然后将另一个字符串附加到新字符串上。
fn main() {
    let owned_string: String = "hello ".to_owned();
    let borrowed_string: &str = "world";
    
    let together = owned_string.clone() + borrowed_string;
    println!("{together}");
}

注意 - 我所做的所有类型规范都是多余的 - 编译器可以推断出这里所涉及的所有类型。我只是为了让刚接触Rust的人更清楚而添加了它们,因为我预计这个问题会受到这个群体的欢迎!

3
你对“Add”/“+”符号有什么看法?如果你愿意,可以谈谈它。 - bluss
也许这很简单,但要理解它需要查看Add与String可能的类型签名。 - bluss
1
@jsalter 这是一个相当独立的话题,因此将其作为另一个顶级问题可能会更好。我已经更新了链接到适当的文档(至少尽可能接近...)。 - Shepmaster
19
@ChrisMorgan 需要注意的是,由于实现特化,上述评论中关于 .to_owned().to_string() 的差异已经得到修复。当在 &str 上调用时,它们现在具有相同的性能。相关提交:https://github.com/rust-lang/rust/pull/32586/files - chad
1
@paddyg 是的,这有点微妙。起始类型都是 String,但是你取一个引用 (&String),它可以被强制转换为 &str。我将整个路径 String -> &String -> &str 都写出来了,因为初学者可能甚至不知道你可以取一个 String 的引用。 :-) - Shepmaster
显示剩余4条评论

112

要将多个字符串连接成一个字符串,用另一个字符分隔,有几种方法。

我看到过最好的方法是使用数组上的 join 方法:

fn main() {
    let a = "Hello";
    let b = "world";
    let result = [a, b].join("\n");

    print!("{}", result);
}

根据您的使用情况,您可能更喜欢拥有更多的控制:

fn main() {
    let a = "Hello";
    let b = "world";
    let result = format!("{}\n{}", a, b);

    print!("{}", result);
}

我见过一些更多的手动方法,有些避免了这里或那里的一两个分配。为了可读性,我认为上述两种方式已经足够。


join 的文档在哪里?它似乎处于数组和字符串之间。我在 array 文档中搜索,但很快就感到困惑了。 - Duane J
3
join实际上是附加在SliceContactExt特质上的。该特质被标记为不稳定,但其方法是稳定的,而且包含在预导入模块中,因此默认情况下可以在任何地方使用。团队似乎很清楚该特质不需要存在,并且我想未来会有改变。 - Simon Whitehead
8
也许你应该提到,对于连接两个str,使用joins1.to_owned().push_str(s2)更高效,因为它避免了第二次分配内存。 - Ibraheem Ahmed

88

在Rust中连接字符串的简单方法

Rust中有多种可用的方法来连接字符串。

第一种方法(使用concat!()):

fn main() {
    println!("{}", concat!("a", "b"))
}
上面代码的输出是:

ab


第二种方法(使用push_str()+运算符):

fn main() {
    let mut _a = "a".to_string();
    let _b = "b".to_string();
    let _c = "c".to_string();

    _a.push_str(&_b);

    println!("{}", _a);

    println!("{}", _a + &_c);
}
上面代码的输出为:

ab

abc


第三种方法(使用format!()函数):

fn main() {
    let mut _a = "a".to_string();
    let _b = "b".to_string();
    let _c = format!("{}{}", _a, _b);

    println!("{}", _c);
}

以上代码的输出为:

ab

请查看并在Rust playground上进行实验。


10
这个回答并没有为现有的答案增添任何新内容。 - Shepmaster
28
答案的格式很好,起到了作用。虽然没有添加任何新的东西,但我很高兴@ashwin-rajeev将其简化了。 - fuma
9
concat 只能用于字面量,因此并不是非常有用。 - Abhijit Sarkar

29

我认为在这里也应该提到concat方法和+

assert_eq!(
  ("My".to_owned() + " " + "string"),
  ["My", " ", "string"].concat()
);

还有一个concat!宏,但仅适用于字面量:

let s = concat!("test", 10, 'b', true);
assert_eq!(s, "test10btrue");

2
“+”已经在现有答案中提到了。(这是一个实现Add特质的例子,它将String作为左侧,将&str作为右侧:) - Shepmaster
4
没错,现有的回答非常宽泛,我之前没有注意到。 - suside
2
到目前为止,最好的答案是只使用数组方法或字符串拼接。宏仅用于隐藏某些语法,而不是发明复杂的语法使核心语言变得晦涩。添加特性对对象可能很有用,但至少会让人感到困惑。 - Anssi

25

使用字符串插值进行拼接

更新:截至2021年12月28日,这个功能在Rust 1.58 Beta 中已经可用。您不再需要使用 Rust Nightly 构建来进行字符串插值。(为了保存答案的完整性,其余部分未作更改)

RFC 2795于2019年10月27日发布:

建议支持隐式参数,以实现许多人所知道的“字符串插值”——一种在字符串中嵌入参数以进行拼接的方式。

RFC:https://rust-lang.github.io/rfcs/2795-format-args-implicit-identifiers.html

最新问题状态可以在此处找到: https://github.com/rust-lang/rust/issues/67984

在撰写本文时(2020年9月24日),我认为这个功能应该可以在 Rust Nightly 构建中使用。

这将允许您通过以下简写进行拼接:

format_args!("hello {person}")

它等同于这个:

format_args!("hello {person}", person=person)

还有一个名为"ifmt"的板条箱,它提供了自己的字符串插值方式:

https://crates.io/crates/ifmt


1
现在Rust 1.58 Beta版本已经可用。 - at54321

8

将两个String连接起来:

fn concat_string(a: String, b: String) -> String {
    a + &b
}

连接两个 &str

fn concat_str(a: &str, b: &str) -> String {
    a.to_string() + b
}

3
从Rust 1.58版本开始,您也可以像这样连接两个或多个变量:format!("{a}{b}{c}")。这与format!("{}{}{}", a, b, c)基本相同,但稍微更短一些,并且(可以说)更易于阅读。这些变量可以是String&str(对于其他非字符串类型也是如此)。结果是一个String。 有关更多信息,请参见此处

-3
在Rust中,默认情况下都是关于内存管理、所有权和移动的,我们通常不会看到像复制或深拷贝这样的操作, 如果你想连接字符串,那么左侧应该是可变类型的String,右侧可以是普通的字符串字面量,即类型为String slices。
    fn main (){
            let mut x = String::from("Hello"); // type String
            let y = "World" // type &str
            println!("data printing -------> {}",x+y);



}

官方声明,这是指当您尝试使用算术加号运算符时 在此输入图像描述


-9
fn main() {
    let a = String::from("Name");
    let b = "Pkgamer";
    println!("{}",a+b)
}

4
如果你在想为什么会被踩票:你没有提供任何解释性的文字,你的回答并没有完全回答问题(那三种 &str/String 的组合怎么办呢?),而且你的回答并没有比例如 这个 更有价值。 - Caesar

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