如何将 Vec<> 传播到 format!() 的参数中?

How can I spread a Vec<> into the arguments of format!()?

我有 Vec 个字符串(strString),我想将它们用作 format!() 的参数。如果 JS 的 ... 语法可用,我会做这样的事情:

let data = vec!["A", "B", "C"];
let result = format!("{} says hello to {} but not to {}", ...data);

Rust 中是否有任何替代方案可以使这样的事情成为可能,并且理想情况下不会非常冗长?

我认为部分困难是 Vec 可能没有正确数量的参数,所以如果它有错误的数量,它会恐慌,我可以接受。

您不能像在 jspython 中那样展开它们。 但是您可以在 Vec<String>Vec<&str>:

上使用 join
let data = vec!["A", "B", "C"];
let result = data.join("->");

Playground

根据 nightly 你可以使用 intersperse_with 和迭代器的组合:

    let data = vec!["A", "B", "C"];
    let phrases = vec![" says hello to ", " but not to "];
    let mut separators = phrases.iter().map(|x| x.to_string());
    let result = data
        .iter()
        .map(|x| x.to_string())
        .intersperse_with(|| separators.next().unwrap())
        .collect::<String>();

Playground

如果不手动编写大量代码或使用过程宏生成必要的代码,目前无法做到这一点。

作为更简单的解决方法,您可以尝试使用 dynfmt,它提供了动态传递参数的方法。

use dynfmt::{Format, SimpleCurlyFormat};

let formatted = SimpleCurlyFormat.format("{} says hello to {} but not to {}", &["A", "B", "C"]);
assert_eq!("A says hello to B but not to C", formatted.expect("formatting failed"));

dyn-fmt crate 看起来正是我需要的。它指定了一个特性,该特性将 format() 方法添加到字符串,该方法采用 Iterator。任何额外的参数都将被忽略,缺少的参数将被替换为空字符串,因此它不会出现恐慌。如果您不需要 format!() 的各种格式选项,那么它看起来是一个非常好的可靠选项。

use dyn_fmt::AsStrFormatExt;
let data = vec!["A", "B", "C"];
let result = "{} says hello to {} but not to {}".format(data);
assert_eq!(result, "A says hello to B but not to C");

我认为在@Netwave 的回答中生成 Vec<String> 是一种浪费,所以我使用 iter.flat_map():

对其进行了改进
fn flat_map_niave(b: &mut Bencher) {
    let data = vec!["A", "B", "C"];
    let separators = vec![" says hello to ", " but not to "];
    b.iter(|| {
        let mut phrases = separators.iter();
        data.iter()
            .intersperse_with(|| phrases.next().unwrap())
            .flat_map(|s| s.chars())
            .collect::<String>()
    });
}

有各种以牺牲可读性为代价提高性能的技巧,我将把它们留在 playground

  • flat_map_improved:使用Vec<u8>String::from_utf8()
  • flat_map_unchecked:使用Vec<u8>String::from_utf8_unchecked()
running 4 tests
test flat_map_improved  ... bench:         134 ns/iter (+/- 17)
test flat_map_niave     ... bench:         145 ns/iter (+/- 9)
test flat_map_unchecked ... bench:         116 ns/iter (+/- 6)
test vec_of_strings     ... bench:         235 ns/iter (+/- 6)