多行字符串的语法是什么?

357

我在学习Rust时遇到了一些困难,不知道字符串语法是如何工作的。具体来说,我正在尝试弄清如何创建多行字符串。


2
可能是如何在Rust中编写多行字符串?的重复问题。 - Jo Liss
老的问题被标记为新的问题的重复有点奇怪。这不应该反过来吗? - BinaryButterfly
1
@BinaryButterfly 我认为这是因为这个问题得到了更多的关注,并且有更清晰的答案。 - TheTechRobo the Nerd
5个回答

449

所有字符串文本都可以跨越多行;例如:

let string = "line one
line two";

是一个两行的字符串,与"line one\nline two"相同(当然,也可以直接使用\n换行转义)。如果你只想为了格式化目的而将一个字符串分成多行,可以使用\来转义换行和前导空格;例如:

let string = "one line \
    written over \
    several";

“一行写成多行”与"one line written over several"意思相同。

如果你想在字符串中添加换行,可以在\之前添加它们:

let string = "multiple\n\
              lines\n\
              with\n\
              indentation";

它与"multiple\nlines\nwith\nindentation";相同。


24
如果只是可读性的问题,我想添加concat!()宏以完成给定的选项(https://doc.rust-lang.org/std/macro.concat.html)。 - hakononakani
如果我想要保留下一行的空格而不是在前一个反斜杠之前,那怎么办?这是否可能,还是它总是会被“吃掉”,因为它被认为是缩进的一部分? - theberzi
3
@theberzi,是的,保留空格的唯一方法是将其放在“\”之前,因为下一行会吃掉所有前导空格。 - huon
看起来,尾随的反斜杠只会删除下一行开头的空格字符,而不是所有空格直到第一个字符(例如,如果您使用制表符缩进,则它们不会被删除)。 - BallpointBen

258
如果你想做一些稍微长一点的事情,可能包括引用、反斜杠等等,可以使用原始字符串字面值表示法
let shader = r#"
    #version 330

    in vec4 v_color;
    out vec4 color;

    void main() {
        color = v_color;
    };
"#;

输出:

    #version 330

    in vec4 v_color;
    out vec4 color;

    void main() {
        color = v_color;
    };

游乐场链接


如果您的字符串中有连续的双引号和井号序列,您可以将任意数量的井号作为分隔符。
let crazy_raw_string = r###"
    My fingers #"
    can#"#t stop "#"" hitting
    hash##"#
"###;

这将输出:

    My fingers #"
    can#"#t stop "#"" hitting
    hash##"#

3
但是,只有在你不想要换行符时才需要转义换行符,而原始字符串对此没有帮助。 - poolie
2
原始字符串只是为了让你不必在每行末尾添加 '',如果你不关心换行符(例如当你嵌入代码时,它是与换行符无关的,比如着色器和内核),或者正如你所暗示的那样,当换行符实际上是必要的。它只是使嵌入代码更容易,你可能想要编辑而不必费力在每行末尾加 ''。就这样。 - c0g
2
如果您想要(或不介意)在结果字符串中有换行符,普通的双引号字符串就可以完美地实现,就像其他示例中所示。如果您想避免换行符,则原始字符串无法胜任。它们只有在文本包含引号、反斜杠等内容时才有用 - 这可能会在嵌入式源代码中发生。 - poolie
2
我现在明白你的意思了,你是完全正确的。 - c0g
13
展示打印这些字符串的输出将有所帮助。从答案本身无法确定换行符的处理方式以及缩进如何处理。 - bluenote10
我已经点了踩,因为这个答案对我来说太模糊了。正如提到的那样,没有提及前导缩进是如何处理的。 - rv.kvetch

117

Huon's answer 是正确的,但如果缩进让你感到困扰,可以考虑使用 Indoc 这个工具,它是一个用于缩进多行字符串的过程宏。Indoc 代表 "Indented Document",它提供了一个名为 indoc!() 的宏,它接受一个多行字符串文本并取消缩进,使最左侧的非空格字符位于第一列。

let s = indoc! {"
    line one
    line two
"};

结果为"line one\nline two\n"

相对于文档最左边的非空格字符,保留空白符,因此以下内容中第二行相对于第一行缩进了3个空格:

let s = indoc! {"
    line one
       line two
"};

结果为 "line one\n line two\n"


7
啊!非常感谢您!我正要创建自己的问题,特别是关于如何很好地保留代码缩进的多行字符串文字的最佳方法,结果偶然间看到了您在这里的答案!为了参考,我创建了一个Rust Playground,列出了我想到的各种选项及其相对不足之处:https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=c9ca0febb5f154e30811806873e38716 - Paul Molodowitch
2
这个确切的问题在我使用的几乎每种语言中都困扰着我,总有一些不同的解决方法。真是太好了,能够使用这样友好的宏!谢谢。 - DexieTheSheep
如果你需要字符串插值,请参考indoc::formatdoc! - ecoe

22
如果您想在多行字符串中使用换行符精细控制空格,而不使用外部创建工具,则可以执行以下操作。示例取自我的项目。
impl Display for OCPRecData {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "OCPRecData {{\n\
            \x20   msg: {:?}\n\
            \x20   device_name: {:?}\n\
            \x20   parent_device_name: {:?}\n\
        }}", self.msg, self.device_name, self.parent_device_name)
    }
}

结果:

OCPRecData {
    msg: Some("Hello World")
    device_name: None
    parent_device_name: None
}
  • \n\在每行代码的结尾创建一个换行符,并丢弃该代码行中的其他空格。
  • \x20(十六进制;10进制32)是ASCII空格,也是字符串中第一个要保留的空格的指示符。
  • \x20\x20\x20\x20\x20 具有相同的效果。

13

如果您想在代码中缩进多行文本:

let s = "first line\n\
    second line\n\
    third line";

println!("Multiline text goes next:\n{}", s);

结果将如下所示:

Multiline text goes next:
first line
second line
third line

2
请明确地使用散文形式说明代码中哪一部分对于这种行为是重要的。 - Shepmaster
1
现在看起来,这似乎没有为已接受的答案添加任何新内容,该答案指出:可以使用\n换行符转义[...]您可以使用{backslash}转义换行符和前导空格。(在代码注释中输入反斜杠非常困难。) - Shepmaster
2
这个评论建议一种将已接受答案中的两个点结合起来的方法:如何生成一个多行字符串,以代码中的多行形式编写,但同时允许其在代码中获得自己的缩进,以便于风格或可读性,而不会在最终字符串中结束缩进。虽然文本中没有明确说明,但这是一个常见的用例,因此我认为这是一个有价值的建议。(请参见 dtolnay 的答案中的 crate 版本。) - Dato
回复Dato的评论:“请参考dtolnay的答案,了解这个crate版本的内容”:需要明确的是,Indoc与此处给出的示例不同,因为indoc!会保留缩进。 - David J.

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