And observes that this additional feature is needed to avoid dangling references. And, as a long time C++ programmer, this illustrates one of the things I dislike most about C++. In most languages, if you make a little mistake involving mixing up something that references something else with something that contains a copy, you end up with potential overhead or maybe accidental mutation. In Rust, you get a compiler error. In C++, you get use-after-free, and the code often even seems to work!
So now we expect people to type:
auto s = f"{foo}";
And those people expect s to act like a string. But the designers (reasonably!) do not want f to unconditionally produce an actual std::string for efficiency reasons, so there’s a proposal to allow f to produce a reference-like type (that’s a class value, not actually a reference), but for s to actually be std::string.
But, of course, more advanced users might know what they’re doing and want to bypass this hack, so:
explicit auto s = f"{foo}";
Does what they programmer actually typed: s captures foo by reference.
What could possibly go wrong?
(Rust IMO gets this exactly right: shared xor mutable means plus disallowing code that would be undefined behavior means that the cases like this where the code might do the wrong thing don’t compile. Critically, none of this actually strictly requires Rust’s approach to memory management, although a GC’d version might end up with (deterministic) runtime errors instead unless some extra work is done to have stronger static checking. And I think other languages should learn from this.)
> But, of course, more advanced users might know what they’re doing and want to bypass this hack, so:
explicit auto s = f"{foo}";
> Does what they programmer actually typed, so s captures foo by reference.
Wouldn't this problem be best solved by... not declaring s to have a guess-what-I-mean type? If you want to be explicit about the type of s, why not just say what that type is? Wouldn't that be even more explicit than "explicit auto"?
A general issue with C++ (and many statically typed languages with generic) is hilariously long type names that may even be implementation details. Using auto can be a huge time saver and even necessary for some generic code. And people get in the habit of using it.
There are many cases in C++ where the type is unspecified (std::bind for example), or even is unutterable (lambdas, or unnamed structures). I think F-strings would be an example of the latter.
You can always box of course (std::function, std::any at the limit), but it has a non-trivial cost.
IOW I believe it's the same thing as Rust's format_args! macro, but trying to get away without needing a separate format! macro by using implicit conversions.
std::format_args! gets you a Arguments<'a> which we'll note means it has an associated lifetime.
Today-I-learned, Arguments<'a> has a single useful function, which appeared before I learned Rust but only very recently became usable in compile time constants, as_str() -> Option<&'static str>
format_args!("Boo!").as_str() is Some("Boo!")
If you format a literal, this always works, if you format some non-literal the compiler might realise the answer is a compile time fixed string anyway and give you that string, but it might not even if you think it should and no promises are given.
But there is no Arguments::fmt ? Are you thinking of the implementations of Debug::fmt and Display::fmt on Arguments ? Trait implementation isn't the same kind of thing at all.
There is exactly one useful thing you can do with an `Arguments` object: call `.fmt()` on it.
The whole reason for std::Arguments very existence is to call `std::Arguments::fmt` on it.
`.fmt()` is a trait implementation, but that doesn’t change anything (not sure what “kind of thing” refers to here). It’s still a function on std::Arguments.
I think you're quite muddled about what's going on here
The full name of this type is std::fmt::Arguments not std::Arguments and even so there's no such thing as std::fmt::Arguments::fmt - there is no function with that name, we can only talk about this name (since it doesn't exist) if we bring into context a specific trait such as Display or Debug
So the full name of the thing you think is the "one useful thing you can do with Arguments" is
<std::fmt::Arguments as std::fmt::Display>::fmt
or perhaps it's
<std::fmt::Arguments as std::fmt::Debug>::fmt
... as I said, Arguments implements both traits, and their sole function has the same name so we need to disambiguate somehow if we mean one of these functions or the other. For the function defined on Arguments itself, as_str, it's already unambiguous.
In the end the Debug and Display traits are all just ductwork, which is why as_str caught my attention.
It is not a shortcut because it can't be implemented without knowing the `Arguments` internals. `format_args!("{}", "boo").as_str()` returns None for example.
It’s a shortcut in the sense that most, if not all optimisations are shortcuts. This one allows you to shortcut the usual formatting machinery if the result of formatting is a static string.
Like all shortcuts, it’s not something you can always rely on.
Interesting viewpoint: I see this as a distinction without a difference. I’m interested to know why you see it differently? What is its use, if not as a shortcut?
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p33...
And observes that this additional feature is needed to avoid dangling references. And, as a long time C++ programmer, this illustrates one of the things I dislike most about C++. In most languages, if you make a little mistake involving mixing up something that references something else with something that contains a copy, you end up with potential overhead or maybe accidental mutation. In Rust, you get a compiler error. In C++, you get use-after-free, and the code often even seems to work!
So now we expect people to type:
And those people expect s to act like a string. But the designers (reasonably!) do not want f to unconditionally produce an actual std::string for efficiency reasons, so there’s a proposal to allow f to produce a reference-like type (that’s a class value, not actually a reference), but for s to actually be std::string.But, of course, more advanced users might know what they’re doing and want to bypass this hack, so:
Does what they programmer actually typed: s captures foo by reference.What could possibly go wrong?
(Rust IMO gets this exactly right: shared xor mutable means plus disallowing code that would be undefined behavior means that the cases like this where the code might do the wrong thing don’t compile. Critically, none of this actually strictly requires Rust’s approach to memory management, although a GC’d version might end up with (deterministic) runtime errors instead unless some extra work is done to have stronger static checking. And I think other languages should learn from this.)