什么是"标准"连接字符串的方法?

时间:2015-07-10 02:03:00

标签: rust

虽然我基本上理解strstd::string::String是什么以及它们如何相互关联,但我发现从各个部分组成字符串而不花费太多时间并考虑它有点麻烦。像往常一样,我怀疑我还没有看到正确的方法,这使它变得直观和轻而易举。

let mut s = std::string::String::with_capacity(200);
let precTimeToJSON = | pt : prectime::PrecTime, isLast : bool | {
    s.push_str(
        "{ \"sec\": " 
       + &(pt.sec.to_string()) 
       + " \"usec\": " 
       + &(pt.usec.to_string()) 
       + if isLast {"}"} else {"},"})
    };    

上面的代码受到编译器的尊重,例如:

  

src \ main.rs:25:20:25:33错误:二进制操作+无法应用于&'static str类型[E0369]

即使经过半个小时的摆弄并随意添加&,我也无法将其编辑。所以,我的问题在这里:

  • 为了达到显而易见的目的,我需要写些什么?
  • 什么是"标准"在Rust中这样做的方法?

3 个答案:

答案 0 :(得分:19)

Rust编译器是正确的(当然):字符串文字没有+运算符。

我相信format!() macro是您尝试做的事情的惯用方法。它使用std::fmt syntax,它基本上由格式化字符串和格式化参数组成( a la C的printf)。对于您的示例,它看起来像这样:

let mut s: String = String::new();
let precTimeToJSON = | pt : prectime::PrecTime, isLast : bool | {
    s = format!("{{ \"sec\": {} \"usec\": {} }}{}",
        pt.sec,
        pt.usec,
        if isLast { "" } else { "," }
    )
};

因为它是一个宏,所以你可以自由地混合参数列表中的类型,只要该类型实现std::fmt::Display trait(对于所有内置类型都是如此)。此外,您必须escape文字{}分别为{{}}。最后,请注意格式字符串必须是字符串文字,因为宏会解析它,扩展代码看起来与原始format!表达式不同。

以上示例中的playground link

另外两点。首先,如果您正在阅读和编写JSON,请查看rustc-serialize等库。它不那么痛苦了!

其次,如果您只想连接&'static str字符串(即字符串文字),则可以使用concat!() macro以零运行时成本完成此操作。在上面的情况下,它不会对你有所帮助,但它可能与其他类似的一样。

答案 1 :(得分:3)

Itertools::format可以帮助您将其写为单个表达式,如果您真的想要。

let times: Vec<PrecTime>; // iterable of PrecTime
let s = format!("{}", times.iter().format(",", |pt, f|
    f(&format_args!(r#"{{ "sec": {}, "usec": {} }}"#, pt.sec, pt.usec))
));

format()使用分隔符,因此只需在那里指定","(如果不需要分隔符,则指定"")。它有点牵扯,因此格式化可以完全是懒惰和可组合的。您接收回调f,并使用&Display值(可以显示格式化的任何内容)回拨。

在这里,我们演示了使用&format_args!()构建可显示值的一个很棒的技巧。如果您也使用debug builder API,这会派上用场。

最后,使用原始字符串,这样我们就不需要以"格式转义内部r#"{{ "sec": {} "usec": {} }}"#。原始字符串由r#""#分隔(可自由选择#)。

Itertools::format()不使用中间分配,它直接传递给基础格式化程序对象。

答案 2 :(得分:2)

你也可以这么疯狂:

fn main() {
    let mut s = std::string::String::with_capacity(200);

    // Have to put this in a block so precTimeToJSON is dropped, see https://doc.rust-lang.org/book/closures.html
    {
        // I have no idea why this has to be mut...
        let mut precTimeToJSON = |sec: u64, usec: u64, isLast: bool| {
            s.push_str(&( // Coerce String to str. See https://doc.rust-lang.org/book/deref-coercions.html
                "{ \"sec\": ".to_string()      // String 
                + &sec.to_string()             // + &str    (& coerces a String to a &str).
                + " \"usec\": "                // + &str
                + &usec.to_string()            // + &str
                + if isLast {"}"} else {"},"}  // + &str
            ));
        };
        precTimeToJSON(30, 20, false);
    }
    println!("{}", &s);
}

基本上,运营商String + &str -> String 已定义,因此您可以执行String + &str + &str + &str + &str。这会为您提供String,您必须使用&str强制回&。我认为这种方式可能效率很低,因为它可能(可能)分配String s的负载。