I have various macros that boil down to format_args!(). They go into one another. Sometimes I also want to append them to an existing String. If I used format!() or .to_string(), it would work, at the price of an unwanted heap allocation.
Instead, what I'd like is the following. Of course, owning neither AddAssign nor String I can't currently do this myself. (For now I have an ugly workaround by wrapping the String.) I think this would make sence as part of std. Not sure about the Err. I hope this concrete usage couldn't cause more trouble than the existing += str.
I'm having a hard time imagining your exact use-case from this vagus description alone. Would you mind elaborating a bit what "boil down to format_args!()" means, and how you use your macros so that you "sometimes append to a String" (and presumably sometimes do something else)? Like, some code example of what the call to the macro would like and how it's improved with such a += implementation. Maybe, also the question "why isn't fmt::write and/or write! sufficient" should be answered.
The problem is that the efficient way is more verbose than less performant ways of appending formatted string:
use std::fmt::Write;
let _ = write!(&mut s, "{x}");
vs
s += &format!("{x}");
or
s.push_str(&format!("{x}"));
While I usually don't like the + overload on strings, I think support for format_args! here is a very clever trick, and gives String a pretty compact and relatively efficient formatted append.
One thing that could help this is if there were an inherent write_fmt(…) -> () on String, then you wouldn’t need to use the trait or discard the return value.
Thanks for the clarification. That's what I think I'd have guessed, too, but I like the more explicit code example to be sure there's no misinterpretation
Would it be possible to have an attribute to suppress the #[must_use] of a type when defining a function with said function as return type? Then String::write_fmt could use this attribute to indicate that it never returns an error.
signature? I.e. supporting not only fmt::Arguments<'_> but simply any Display type.
Probably implemented along the lines of
{
x.fmt(&mut Formatter::new(self)).unwrap()
}
which should be equivalent (but slightly cheaper) compared to
{
self.write_fmt(format_args!("{x}")).unwrap()
}
(it’s a bit like a slight generalization[1] of the ToString (default) implementation; also peeking there, looks like the .unwrap() should instead be “.expect("a Display implementation returned an error unexpectedly")”; and we might also want to mirror, or perhaps even somehow combine, the specialization of ToString to get the same performance; e.g. for &str it should simply call push_str.)
I do like s += format_args!("...");, that seems indeed more discoverable than write!(s, "...").unwrap(). So it seems better to me than what my ACP proposes.
s.push_display(format_args!("...))" also works I guess but that's more nested so a bit harder to type and read.
error[E0277]: cannot add-assign `&String` to `String`
--> src/main.rs:27:7
|
27 | s += &additional;
| ^^ no implementation for `String += &String`
|
= help: the trait `AddAssign<&String>` is not implemented for `String`
= help: the following other types implement trait `AddAssign<Rhs>`:
<String as AddAssign<Arguments<'_>>>
<String as AddAssign<&str>>
Maybe, common use-cases can be saved by also adding extra implementations for RHS types such as &String, but there’d always be at least some degree of breakage left.
Although now, on second thought, I’m kind-of wondering, are there any common crates that come with a AddAssign<CustomType> for String implementation, and if so would helping users of such crates motivate additional implementations for things like &String anyways.
Yet another case where deref coercion during trait resolution would help. Side-note: I haven't tried making that into a full-blown RFC yet because it needs to support more than just deref coercion for things like arrays.
Another less ideal aspect to += is that it raises the question of whether we should support +. If it’s not supported, it makes the String API a bit weird. But if we do support it, what types should/could work with Add?