You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
format_args deduplicates consts with interior mutability or destructor #145739
If this proposal were captured, the following (currently invalid) macro invocation: format_args!("hello {person}") would become a valid macro invocation, and would be equivalent to a shorthand for the already valid: format_args!("hello {person}", person=person)
This desugaring would naturally result in deduplicating consts (the current behavior), since having multiple named arguments with the same name doesn't make much sense.
changed the title [-]`format_args` deduplicates consts with interior mutability[/-][+]`format_args` deduplicates consts with interior mutability or destructor[/+]on Aug 22, 2025
As I see it, this surprise comes from conflict between two different interpretations of the meaning of the syntax. Here are citations for both of these interpretations:
If this proposal were captured, the following (currently invalid) macro invocation:
format_args!("hello {person}")
would become a valid macro invocation, and would be equivalent to a shorthand for the already valid:
format_args!("hello {person}", person=person)
Under this interpretation, the current behavior is exactly correct: there can only be one key-value pair with a given key, so there is only one value expression to evaluate, too.
If a named parameter does not appear in the argument list, format! will reference a variable with that name in the current scope.
This sentence can be taken as saying that captures should not be translated into key-value pairs, but are their own distinct thing which occurs only when there is not a matching key in the arguments. (Or, extremely pedantically, we could say that a const item is not a variable, and so this sentence gives no meaning to mentioning a const item in a format string. But, I think, nobody wants that.)
Under this interpretation, the current behavior is incorrect.
A factor worth noting is that interpretation 2 is more friendly to the possibility of expanding format strings to allow more kinds of expressions than single identifiers. (I personally think doing so would be unwise, but I don’t care to argue that point.)
@ShoyuVanilla Awesome, let us know if you need help, either here, on #149291, or on Zulip #t-lang. Right now we're going to wait on results from your crater experiment; if at any point you feel blocked please let us know so you don't have to wait on us.
I don't think we should change this, but instead accept this as expected behaviour. See #149926 (comment):
I don't think we should do this. It will make the generated code for println!("{x} {x}");less efficient, as it will get two separate arguments instead of one.
I don't want to end up in a situation where it would make sense for Clippy to suggest something like:
warning: using the same placeholder multiple times is inefficient as of Rust 1.94.0
--> src/main.rs:3:5
|
3 | println!("{x} {x}");
| ^^^^^^^^^^^^^^^^^^^
|
help: change this to
|
3 - println!("{x} {x}");
3 + println!("{x} {x}", x = x);
|
Adding , x = x shouldn't make a difference. If adding that makes the resulting code more efficient, I strongly feel like we've done something wrong.
Reacted by Lukas Wirth, David Tolnay, Taylor Cramer and Trevor Gross
Reacted by Kevin Reid, David Tolnay and Taylor Cramer
Note that clippy today already warns about the code example at the top of this issue:
warning: a `const` item should not be interior mutable
--> src/main.rs:14:1
|
14 | const ZERO: PrintCounter = PrintCounter(Cell::new(0));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
warning: a `const` item with interior mutability should not be borrowed
--> src/main.rs:17:16
|
17 | println!("{ZERO}, {ZERO}");
| ^^^^
|
The line in the documentation that misunderstands the behavior that was implemented for format_args! was written by @joshtriplett in #90473 which was also its stabilization PR.
So it does not surprise me that it differs from what was implemented and RFCed, seeing as how it was not written by the person who wrote the RFC and implemented it initially, @davidhewitt.
It does surprise me that T-lang would propose something for stabilization by adding documentation that disagrees with what was actually RFCed and implemented.
I am aware that we have made them easier to encounter over time, but when did
non-idempotent Deref
interior mutability in consts
Drop impls in consts
become good ideas that we wish to make everyone else's code worse for the sake of?
The original RFC that added argument capturing said that it was intentionally avoiding expression interpolation, and there was considerable apprehension towards adding such, especially any form that has arbitrary extension to only limited expressions. I assume, given the author was @davidhewitt, that this was not due to a lack of experience with expression interpolation in a language, but rather because of a surplus of it.
Unsurprisingly, if you try to smuggle some side-effectful code into the format arguments using existing mechanisms for smuggling side-effectful code, it is not exceptionally compatible with the semantics specified in his RFC. However, the code generated is what is preferable.
This behavior seems fairly intentional to me, but perhaps the author will think it merely incidental.
The line in the documentation that misunderstands the behavior that was implemented for format_args! was written by @joshtriplett in #90473 which was also its stabilization PR.
The original RFC that added argument capturing said that it was intentionally avoiding expression interpolation, and there was considerable apprehension towards adding such, especially any form that has arbitrary extension to only limited expressions. I assume, given the author was @davidhewitt, that this was not due to a lack of experience with expression interpolation in a language, but rather because of a surplus of it.
My impression was that that apprehension was not from the original author, but rather from the lang team not wanting to extend to arbitrary expressions for reasons of complexity.
The previous outcome of the lang discussion, to remove this deduplication (for consistency with function arguments) would continue to be my preference, and I'd likewise propose @traviscross's solution of only deduplicating when not observable. But for the record, I would also not object to adjusting rust-lang/rfcs#3626 to be consistent with deduplication; I don't want to block that RFC on this change, and I only updated that RFC in anticipation of this change because lang did not anticipate this change being contentious.
My impression was that that apprehension was not from the original author, but rather from the lang team not wanting to extend to arbitrary expressions for reasons of complexity.
The author also specifically distanced his proposal from any that involved inclusion of arbitrary expressions (broadly called "interpolation" in the RFC discussion) and suggested they can result in poor style: rust-lang/rfcs#2795 (comment)
This was raised before anyone on T-lang did so.
Indeed the discussion involved quite a bit on the matter of "what if interpolation?" by almost everyone who was commenting who wasn't on T-lang before T-lang weighed in with any significance. Perhaps that was merely in the interest of avoiding scope creep, but I don't think it is accurate to characterize that part as even mostly T-lang's idea.
Activity
theemathas commentedon Aug 22, 2025
It is possible that this is working as intended, although this behavior is very strange.
RFC 2795 says:
This desugaring would naturally result in deduplicating consts (the current behavior), since having multiple named arguments with the same name doesn't make much sense.
theemathas commentedon Aug 22, 2025
This also affects consts with destructors.
The following code prints
droponly once, instead of twice:@rustbot labels +A-const-eval +A-destructors
[-]`format_args` deduplicates consts with interior mutability[/-][+]`format_args` deduplicates consts with interior mutability or destructor[/+]kpreid commentedon Aug 22, 2025
As I see it, this surprise comes from conflict between two different interpretations of the meaning of the syntax. Here are citations for both of these interpretations:
RFC 2785 says:
Under this interpretation, the current behavior is exactly correct: there can only be one key-value pair with a given key, so there is only one value expression to evaluate, too.
On the other hand, the standard library documentation says:
This sentence can be taken as saying that captures should not be translated into key-value pairs, but are their own distinct thing which occurs only when there is not a matching key in the arguments. (Or, extremely pedantically, we could say that a
constitem is not a variable, and so this sentence gives no meaning to mentioning aconstitem in a format string. But, I think, nobody wants that.)Under this interpretation, the current behavior is incorrect.
A factor worth noting is that interpretation 2 is more friendly to the possibility of expanding format strings to allow more kinds of expressions than single identifiers. (I personally think doing so would be unwise, but I don’t care to argue that point.)
18 remaining items
Auto merge of #149291 - ShoyuVanilla:crater-issue-145739, r=<try>
joshtriplett commentedon Nov 26, 2025
@ShoyuVanilla Awesome, let us know if you need help, either here, on #149291, or on Zulip #t-lang. Right now we're going to wait on results from your crater experiment; if at any point you feel blocked please let us know so you don't have to wait on us.
Implement a crater lint for rust-lang#145739
ShoyuVanilla commentedon Dec 14, 2025
The crater result looks fine, so I've opened #149926
m-ou-se commentedon Dec 24, 2025
I don't think we should change this, but instead accept this as expected behaviour. See #149926 (comment):
m-ou-se commentedon Dec 24, 2025
Note that clippy today already warns about the code example at the top of this issue:
workingjubilee commentedon Dec 25, 2025
The line in the documentation that misunderstands the behavior that was implemented for
format_args!was written by @joshtriplett in #90473 which was also its stabilization PR.So it does not surprise me that it differs from what was implemented and RFCed, seeing as how it was not written by the person who wrote the RFC and implemented it initially, @davidhewitt.
It does surprise me that T-lang would propose something for stabilization by adding documentation that disagrees with what was actually RFCed and implemented.
I am aware that we have made them easier to encounter over time, but when did
become good ideas that we wish to make everyone else's code worse for the sake of?
The original RFC that added argument capturing said that it was intentionally avoiding expression interpolation, and there was considerable apprehension towards adding such, especially any form that has arbitrary extension to only limited expressions. I assume, given the author was @davidhewitt, that this was not due to a lack of experience with expression interpolation in a language, but rather because of a surplus of it.
Unsurprisingly, if you try to smuggle some side-effectful code into the format arguments using existing mechanisms for smuggling side-effectful code, it is not exceptionally compatible with the semantics specified in his RFC. However, the code generated is what is preferable.
This behavior seems fairly intentional to me, but perhaps the author will think it merely incidental.
joshtriplett commentedon Jan 21, 2026
It is not clear to me what line you are referring to. All of the documentation at https://github.com/rust-lang/rust/pull/90473/changes#diff-f299871a8b9896a7ad00ea2a572067b625a3323d34c3152d9ab44da65b491e0c appears to be accurate by either interpretation of
format_args!; none of it depends on the interpretation.My impression was that that apprehension was not from the original author, but rather from the lang team not wanting to extend to arbitrary expressions for reasons of complexity.
joshtriplett commentedon Jan 21, 2026
The previous outcome of the lang discussion, to remove this deduplication (for consistency with function arguments) would continue to be my preference, and I'd likewise propose @traviscross's solution of only deduplicating when not observable. But for the record, I would also not object to adjusting rust-lang/rfcs#3626 to be consistent with deduplication; I don't want to block that RFC on this change, and I only updated that RFC in anticipation of this change because lang did not anticipate this change being contentious.
workingjubilee commentedon Jan 28, 2026
@joshtriplett:
I am not going to repeat @kpreid's argument: #145739 (comment)
The author also specifically distanced his proposal from any that involved inclusion of arbitrary expressions (broadly called "interpolation" in the RFC discussion) and suggested they can result in poor style: rust-lang/rfcs#2795 (comment)
This was raised before anyone on T-lang did so.
Indeed the discussion involved quite a bit on the matter of "what if interpolation?" by almost everyone who was commenting who wasn't on T-lang before T-lang weighed in with any significance. Perhaps that was merely in the interest of avoiding scope creep, but I don't think it is accurate to characterize that part as even mostly T-lang's idea.