We have Add<&str> for String
already along with the AddAssign<&str> for String
, I would expect the same pair here.
Huh. TIL
Just my 2c, but I'm not fond of s += format_args!(...)
(or s.bikeshed(format_args!(...))
for that matter). It feels a bit too magical, and forces the user to learn about format_args
which in all other cases is merely an implementation detail. Also, it's a bit too easy to mix up with s += format!("...")
which has exactly the same sematics, is just less efficient (although I guess a lint would help here).
On the other hand, trying to write!
to a String
may not be immediately obvious, but it should be pretty obvious in retrospect and pleasantly orthogonal. It should just be documented better – currently there's nothing in the String
docs that point to that direction besides the very undiscoverable Write
impl at the very bottom of the page. At least the docs for add_assign
and push_str
could be updated to mention the write!
idiom. There's also the very recently added Clippy lint format_push_string
although it's allow by default.
But using the write!
-trait comes at the inconvenience of an unecessary and unreachable error path. Two potential alternatives:
-
It could live as a method on
fmt::Arguments
. -
The
ToString
trait could have a method accepting an existing buffer for append. It's implemented with a specialization forfmt::Arguments
as-is already, this could then also be sutiable specialized for performance. -
On that note, an appending method on
ToString
would make the usageDisplay
potentially more efficient. Since, asimpl<T: Display> ToString for T
puts it:let mut buf = String::new(); let mut formatter = core::fmt::Formatter::new(&mut buf); // Bypass format_args!() to avoid write_str with zero-length strs fmt::Display::fmt(self, &mut formatter)
Shouldn't it be somewhat certain that the same applies to the
write!
? Since this is, immediately, constructing aformat_args!
internally that would run into w/e issue in codegen the above faces.For argument's sake let's call such a speculative new method
trait ToString { default fn write_string(&self, buffer: &mut String) { //… } }
Yeah, I meant (but failed to explicitly state) that some way of resolving the write!
error situation (even if hackish) would be preferable to the format_args!
alternative. What about simply an annotation like allow(unused_result)
?
Previous discussion here:
Since then, I became even more convinced that this is a trivial papercut which is important to fix in stdlib.
I like += format_args!()
solution because its a clear improvement with basically no downsides. We should go and do it.
I wouldn't want for us to stick with buf += format_args!()
as a long-term (10 years from now) solution --- its a clever use of plumbing and a happy coincidence in library design, but it doesn't seem like a sufficiently good first class solution.
Potential long-term alternative is for rust to gain first-class string interpolation syntax, such that we could write
buf += f"Hello, {name}!"
where f""
desugars into something like format_args!
.
Another thread I created a while ago: Method to append any Display
type to a String
I would really prefer an inherent method over a +=
impl, because
- it is less magical
- it is more general (it can be generic over
Display
, whereas a blanketimpl<T> AddAssign<T> for String where T: Display
is not possible)
In the previous discussion, there were only two open questions: The name, and whether the method should accept T
or &T
(though most people argued for &T
).
I don't see any reason why we shouldn't just do both.
As for the name question, I think push_display
is the obvious choice. While it's a little clunky, it's also very clear from the name what it happening.
write!
is quite magical for Rust. It is completely duck-typed — it doesn't use Write
traits, it takes anything that compiles with .write_fmt(args)
appended to it. It doesn't even care about the function type or return type. It takes even bogus signatures like fn write_fmt(&self, f: impl Sized) -> ()
.
Compared to that format_args!
is pretty tame and well-behaved.
If (eh, can't).format_args!
could be made to borrow its arguments for a longer lifetime…
If a new operator ++
for concatenation strings would be added, then ++=
also could be added
String
s can already be concatenated with +
and +=
(with other strings). What would the operator ++
do differently?
The question here is if it a good idea to allow other right-hand-sides (impl Display
or fmt::Arguments
) for +
and +=
.
As a humble bikeshedding enthusiasist I'm respectfully reminding that String::write()
is not occupied in any popular way I'm aware of.
Are there other parts of the stdlib that are duck typed like write!
?
It's very frustrating to work with and doesn't feel likes regular rust code at all.
It would be much better to have a trait to abstract away the two Writes (std::io::Write and std::fmt::Write)
There is also an RFC for postfix macros which would be another way to tackle this possibly, resulting in something like buffer.write_to!("{x}")
. Not sure if I personally like this though, mainly raising it as another possible option.
I just had a look at that, the problem is that using a qualified path we lose out on the autoref behavior of $buf.write_fmt
It's certainly not great, but format_args!
is definitely already more than an implementation detail, and is already worth learning about for some use-cases. I've written code that calls it directly on multiple occasions when I wanted to avoid the intermediate String
allocations caused by format!
. The name is a bit suboptimal (format_lazy!
or so would better reflect what happens), but oh well.
This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.