While I understand basically what str
and std::string::String
are and how they relate to each other, I find it a bit cumbersome to compose strings out of various parts without spending too much time and thought on it. So as usual I suspect I did not see the proper way to do it yet, which makes it intuitive and a breeze.
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 {"},"})
};
The code above is honored by the compiler with error messages like:
src\main.rs:25:20: 25:33 error: binary operation
+
cannot be applied to type&'static str
[E0369]
And even after half an hours worth of fiddling and randomly adding &
, I could not make this compilable. So, here my questions:
- What do I have to write to achieve the obvious?
- What is the "standard" way to do this in Rust?
Itertools::format
can help you write this as a single expression if you really want to.format()
uses a separator, so just specify","
there (or""
if you need no separator). It's a bit involved so that the formatting can be completely lazy and composable. You receive a callbackf
that you call back with a&Display
value (anything that can be Display formatted).Here we demonstrate this great trick of using
&format_args!()
to construct a displayable value. This is something that comes in handy if you use the debug builder API as well.Finally, use a raw string so that we don't need to escape the inner
"
in the format:r#"{{ "sec": {} "usec": {} }}"#
. Raw strings are delimited byr#"
and"#
(free choice of number of#
).Itertools::format()
uses no intermediate allocations, it is all directly passed on to the underlying formatter object.The Rust compiler is right (of course): there's no
+
operator for string literals.I believe the
format!()
macro is the idiomatic way to do what you're trying to do. It uses thestd::fmt
syntax, which essentially consists of a formatting string and the arguments to format (a la C'sprintf
). For your example, it would look something like this:Because it's a macro, you can intermix types in the argument list freely, so long as the type implements the
std::fmt::Display
trait (which is true for all built-in types). Also, you must escape literal{
and}
as{{
and}}
, respectively. Last, note that the format string must be a string literal, because the macro parses it and the expanded code looks nothing like the originalformat!
expression.Here's a playground link to the above example.
Two more points for you. First, if you're reading and writing JSON, have a look at a library such as rustc-serialize. It's much less painful!
Second, if you just want to concatenate
&'static str
strings (that is, string literals), you can do that with zero run-time cost with theconcat!()
macro. It won't help you in your case above, but it might with other similar ones.You can also do this madness:
Basically the operator
String + &str -> String
is defined, so you can doString + &str + &str + &str + &str
. That gives you aString
which you have to coerce back to a&str
using&
. I think this way is probably quite inefficient though as it will (possibly) allocate loads ofString
s.