Skip to content

Postfix macro syntax for methods via opt-in attribute: obj.method!(...) → obj.method(MACRO!(...)) #3904

Open

Description

@OuYangPaste

I’d like to propose a small but meaningful ergonomic improvement that bridges Rust’s powerful macro system with its object-oriented method syntax—without blurring their responsibilities.

The idea is simple: allow a method to be annotated with #[postfix_macro(M)], so that calling obj.method!(args...) automatically expands to obj.method(M!(args...)) at parse time.

Crucially:

  • The macro M remains a completely ordinary, free-standing macro. It could be format_args!, serde_json::json!, or any user-defined macro. It is not tied to the type, doesn’t need special awareness of self, and can be used anywhere else in the codebase.
  • The method remains a normal Rust method: it has a regular signature, shows up in documentation, can be called directly (e.g., logger.info(my_preformatted_args)), and contains all the real logic—like checking log levels, writing to a buffer, or validating state.

In other words: the macro handles compile-time argument preprocessing; the method handles runtime behavior. They stay separate in definition, but combine naturally at the call site.

Why does this matter? Consider logging:

// Today
info!(logger, "user {} connected", name);

// With this proposal
logger.info!("user {} connected", name);

The second version reads more fluently because the action (info!) is attached to its context (logger). Yet under the hood, nothing magical happens: it’s just logger.info(format_args!(...)).

This pattern applies beyond logging—to serialization (obj.serialize!( { x: 1 } )), validation, GPU kernels, and more. All these cases share the same structure: a macro processes arguments at compile time, and a method acts on them at runtime.

How this differs from RFC #2442

This is not a revival of RFC #2442 ("Postfix macros"), which proposed allowing any macro to be called in postfix position (e.g., expr.any_macro!(...)). That approach was rightly rejected because it:

  • Created ambiguity between methods and macros;
  • Allowed arbitrary macros to “attach” to any type without the type author’s consent;
  • Risked namespace pollution and hidden coupling.

In contrast, this proposal is explicit, opt-in, and scoped:

  • Only methods explicitly annotated with #[postfix_macro] gain the ! syntax;
  • The macro is chosen by the type author, not the caller;
  • No new naming rules or resolution semantics are introduced.

It’s less about “postfix macros” and more about giving method authors a way to offer a macro-powered shorthand—while keeping full control.

Safety & minimalism

  • Fully backward compatible;
  • Requires only a parser-level desugaring (like ?);
  • Zero runtime cost;
  • No changes to Rust’s macro or trait systems.

This isn’t about making macros “look like methods.”
It’s about letting each do what it’s best at—and compose them cleanly where it matters most: in user code.

Activity

  1. programmerjake commented on Jan 4, 2026

    @programmerjake
    Member

    This is not a revival of RFC #2442 ("Postfix macros"), which proposed allowing any macro to be called in postfix position (e.g., expr.any_macro!(...)). That approach was rightly rejected because it

    there has not been an official accept/reject decision for #2442 yet.

  2. owenthewizard commented on Jan 15, 2026

    @owenthewizard

    This is AI.

  3. ionicmc-rs commented on Jan 22, 2026

    @ionicmc-rs

    Im sorry, but this is obviously written by ChatGPT (or you use it too much that you write like it).

  4. kennytm commented on Jan 23, 2026

    @kennytm
    Member

    (meta) I don't think using AI means an RFC should be automatically disqualified. And some people not fluent in English may use them as translation tool. This one here also sounds interesting, though I'd still prefer #2442 over this one. In any case Pre-RFC should better be discussed in https://internals.rust-lang.org rather than as an issue as per #2299 and #3756.

  5. OuYangPaste commented on Jan 27, 2026

    @OuYangPaste
    Author

    Sorry, English isn’t my first language. This is just a rough Rust idea I had. Tbh, I was too lazy to write it up in my broken English, so I just gave the core thought to ChatGPT to polish... Whatever, still think it’s a fun approach.(And actually, this sentence was AI-translated too.)

    Im sorry, but this is obviously written by ChatGPT (or you use it too much that you write like it).

  6. ionicmc-rs commented on Jan 29, 2026

    @ionicmc-rs

    Sorry, English isn’t my first language. This is just a rough Rust idea I had. Tbh, I was too lazy to write it up in my broken English, so I just gave the core thought to ChatGPT to polish... Whatever, still think it’s a fun approach.(And actually, this sentence was AI-translated too.)

    Im sorry, but this is obviously written by ChatGPT (or you use it too much that you write like it).

    You should still write it yourself, chatgpt is always just wrong. Broken english is better than AI

  7. Nadrieril commented on Jun 6, 2026

    @Nadrieril
    Member

    The difficulty with your proposal is that we would then need type information before we're done with macro-expansion. This isn't impossible in theory, but in the practice of how the compiler is structured, such proposals have generally been thought to be practically impossible, or at least an immense amount of engineering work.

    As a meta-note, early feature discussion is more appropriate on the forum; you'll find better feedback over there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions