Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stabilize format_args_ln! #97658

Closed

Conversation

joshtriplett
Copy link
Member

@joshtriplett joshtriplett commented Jun 2, 2022

format_args_ln! (formerly format_args_nl!) is generally useful for implementations of formatting macros, and it has clear semantics and a clear interface.

@rustbot rustbot added T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Jun 2, 2022
@rust-highfive
Copy link
Collaborator

Hey! It looks like you've submitted a new PR for the library teams!

If this PR contains changes to any rust-lang/rust public library APIs then please comment with r? rust-lang/libs-api @rustbot label +T-libs-api -T-libs to request review from a libs-api team reviewer. If you're unsure where your change falls no worries, just leave it as is and the reviewer will take a look and make a decision to forward on if necessary.

Examples of T-libs-api changes:

  • Stabilizing library features
  • Introducing insta-stable changes such as new implementations of existing stable traits on existing stable types
  • Introducing new or changing existing unstable library APIs (excluding permanently unstable features / features without a tracking issue)
  • Changing public documentation in ways that create new stability guarantees
  • Changing observable runtime behavior of library APIs

@rust-highfive
Copy link
Collaborator

r? @m-ou-se

(rust-highfive has picked a reviewer for you, use r? to override)

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Jun 2, 2022
@joshtriplett joshtriplett added T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. and removed T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Jun 2, 2022
@CryZe
Copy link
Contributor

CryZe commented Jun 2, 2022

Shouldn't it be format_args_ln like the other macros: println, writeln? (arguably even without the underscore)

@bstrie
Copy link
Contributor

bstrie commented Jun 2, 2022

This seems awfully specific. Since the stdlib implements formatting macros, can we see a diff of what sort of improvement resulted/would result from using this?

@joshtriplett
Copy link
Member Author

joshtriplett commented Jun 2, 2022

Shouldn't it be format_args_ln like the other macros: println, writeln? (arguably even without the underscore)

This came up on Zulip as well. println is "print line", and writeln is "write line", but format_args_nl isn't really "format args line", it's more "format args with newline". "line" maps to "ln", while "newline" maps to "nl". That said, we could change this, either to _ln or to _newline, but I don't think that's worth the associated churn, and I don't think the current name is unclear. (Also, rustc will do a great job of "did you mean" if someone types it the other way.)

So, I personally think this should keep its current name, but I also don't care strongly and would not block changing it if others feel it should be changed.

@joshtriplett
Copy link
Member Author

This seems awfully specific. Since the stdlib implements formatting macros, can we see a diff of what sort of improvement resulted/would result from using this?

std already uses this in println and writeln, via allow_internal_unstable. The benefit of stabilizing it would be for other formatting macros in the ecosystem to be able to use it as well, such as async-std. format_args_nl also has the advantage of generating a single std::fmt::Arguments, which can subsequently be passed around. This also makes it easier for implementations to end up with a single write call or equivalent, rather than a than a second one for the \n byte.

Currently, if you strace a program doing println!("Hello world!\n"), it'll generate one write syscall; if you strace a program calling async_std::println!("Hello world!\n").await, it generates two write syscalls.

@bstrie
Copy link
Contributor

bstrie commented Jun 2, 2022

I'm generally against new macros being defined in the stdlib root, and especially so if it's as niche as this one. Can we define it in std::fmt instead?

@inquisitivecrystal
Copy link
Contributor

Shouldn't it be format_args_ln like the other macros: println, writeln? (arguably even without the underscore)

This came up on Zulip as well. println is "print line", and writeln is "write line", but format_args_nl isn't really "format args line", it's more "format args with newline". "line" maps to "ln", while "newline" maps to "nl". That said, we could change this, either to _ln or to _newline, but I don't think that's worth the associated churn, and I don't think the current name is unclear. (Also, rustc will do a great job of "did you mean" if someone types it the other way.)

So, I personally think this should keep its current name, but I also don't care strongly and would not block changing it if others feel it should be changed.

While it may make sense for it to be nl in a vacuum, that's so close to ln and the semantics are so similar that I think it's likely to be a source of confusion forever. If someone ever reaches for this, they're going to reach for ln just because that's parallel to everything else. All of the confusion can be avoided by making it consistent now. I think this is an instance where consistency is more valuable than strict correctness.

@joshtriplett
Copy link
Member Author

I'm generally against new macros being defined in the stdlib root, and especially so if it's as niche as this one. Can we define it in std::fmt instead?

That seems likely to be a persistent source of confusion, since format_args is in the root.

@yoshuawuyts
Copy link
Member

I'm generally against new macros being defined in the stdlib root, and especially so if it's as niche as this one. Can we define it in std::fmt instead?

That seems likely to be a persistent source of confusion, since format_args is in the root.

I've been meaning to finish up the RFC to (re-)export macros from their logical submodules. We're already well north of 50 macros exported from the std root module, that it doesn't seem too big an issue if we add another to the crate root for consistency. Here's the sequence I propose:

  1. We stabilize this API as one of std::format_args_{ln,nl}.
  2. We finish up the macro re-export RFC and get it through the review process 1.
  3. Once accepted, we re-expose all existing macros in the crate root from their corresponding submodules.
  4. (Further in the future) we figure out a process to potentially deprecate exposing macros from the crate root. This might be tricky, so we intentionally separate "move macros over and establish policy about how to export new macros" from "if and how to deprecate existing macros from the crate root".

Using this approach at every step of the way all format_*! macros will remain co-located, and we make decisions which affect all of them. This stabilization indeed does add another macro to the set, but because the set is already quite large I don't think it matters too much. Does this seem like a workable approach to folks?

Footnotes

  1. I wanted to do this back in April, but I got rather ill and wasn't able to work for over two months. I'm still ramping my workload back up; help with getting this RFC out the door would be most welcome!

@bstrie
Copy link
Contributor

bstrie commented Jun 3, 2022

That seems likely to be a persistent source of confusion, since format_args is in the root.

Rather, it just means that we should be exposing format_args from std::fmt as well. The exposure of macros from the crate root is a legacy technical limitation that we are no longer beholden to, so let's not perpetuate the mistake now that it is no longer required.

@inquisitivecrystal
Copy link
Contributor

inquisitivecrystal commented Jun 4, 2022

That seems likely to be a persistent source of confusion, since format_args is in the root.

Rather, it just means that we should be exposing format_args from std::fmt as well. The exposure of macros from the crate root is a legacy technical limitation that we are no longer beholden to, so let's not perpetuate the mistake now that it is no longer required.

I'm not seeing how this solves the problem? If format_args is exposed from both the crate root and std::fmt, then full consistency would require that this macro also be exposed from both. Otherwise, the set of places they're accessible from would be inconsistent, and could potentially cause confusion.

@joshtriplett joshtriplett force-pushed the stabilize-format-args-nl branch from 39eba2f to 351a13c Compare June 5, 2022 03:40
@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@joshtriplett joshtriplett force-pushed the stabilize-format-args-nl branch from 76379a7 to f9c8876 Compare June 5, 2022 04:10
@rust-log-analyzer

This comment has been minimized.

This provides consistency with `writeln!` and `println!` and
`eprintln!`.
`format_args_ln!` is generally useful for implementations of formatting
macros, and it has clear semantics and a clear interface.
@joshtriplett joshtriplett force-pushed the stabilize-format-args-nl branch from f9c8876 to ef7c7a1 Compare June 5, 2022 06:10
@joshtriplett joshtriplett changed the title Stabilize format_args_nl! Stabilize format_args_ln! Jun 5, 2022
@joshtriplett
Copy link
Member Author

I've rewritten this PR to rename format_args_nl! to format_args_ln!, and then stabilize it.

@joshtriplett joshtriplett removed the T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. label Jun 6, 2022
@inquisitivecrystal inquisitivecrystal added the relnotes Marks issues that should be documented in the release notes of the next release. label Jun 7, 2022
@inquisitivecrystal inquisitivecrystal added the needs-fcp This change is insta-stable, so needs a completed FCP to proceed. label Jun 7, 2022
@scottmcm
Copy link
Member

scottmcm commented Jun 8, 2022

I am totally fine with format_args_ln -- the consistency argument makes sense to me.


But because of https://internals.rust-lang.org/t/null-consistency/16767?u=scottmcm, I was looking at https://www.unicode.org/Public/14.0.0/ucd/NameAliases.txt today:

0009;TAB;abbreviation
000A;LINE FEED;control
000A;NEW LINE;control
000A;END OF LINE;control
000A;LF;abbreviation
000A;NL;abbreviation
000A;EOL;abbreviation
000B;LINE TABULATION;control

Where, interestingly, NL is an official abbreviation for LINE FEED, but LN is not.

I think consistency with println and friends trumps following unicode here, but _nl is arguably technically correct.

@joshtriplett
Copy link
Member Author

@scottmcm That was my original argument as well, but I think consistency with println and writeln is more important.

@m-ou-se
Copy link
Member

m-ou-se commented Jun 8, 2022

I'm not convinced we should stabilize this. I'd prefer if we worked on something like #78356, to make sure that format_args!("{}\n", format_args!(...)) gets optimized to something just as efficient.

println and friends used to use concat!() to append "\n" to the format string. format_args_nl was only added as a workaround for the problem caused by how concat automatically converts non-string literals to strings. (println!(5) would expand to something with concat!(5, "\n") which would silently just produce "5\n".) Many formatting macros in the ecosystem don't just append a \n, but add some other prefix and/or suffix.

@joshtriplett
Copy link
Member Author

I would love to see further optimizations to format_args, but I don't think that future possibility obsoletes this. This is a simple solution that exists today and that we're already using ourselves, and that other libraries would like to make use of. If we find a more general solution in the future, that wouldn't obsolete this common case any more than it would obsolete writeln or println.

@liigo
Copy link
Contributor

liigo commented Jul 7, 2022

format_args_ln should be named as formatln_args, see:

print and println
eprint and eprintln
write and writeln
format and formatln
format_args and formatln_args

formatln should also be added alongside with formatln_args, to make the table complete.

@PG-MANA
Copy link

PG-MANA commented Jul 16, 2022

I hope format_args_ln(format_args_nl) is stabilized.

When I write bare metal programs with the core crate, I must implement println myself.
Then, println implemented with format_args_nl is usable with the below code.

(println!("The number is {number}"); is not compilable with the println implemented with format_args! and concat!)

As already commented, we can implement println with format_args!("{}\n", format_args!($fmt)) and it works with the below code, but I think format_args_nl is smarter currently. (I also would like to see further improvement of format_args.)

fn print_args(args: fmt::Arguments) {
   /* */
}

macro_rules! println {
    ($fmt:expr) => ($crate::print_args(format_args_nl!($fmt)));
    ($fmt:expr, $($arg:tt)*) => ($crate::print_args(format_args_nl!($fmt, $($arg)*)));
}

fn main() {
    let number = 1;
    println!("The number is {number}");
}

@bors
Copy link
Contributor

bors commented Jul 19, 2022

☔ The latest upstream changes (presumably #99451) made this pull request unmergeable. Please resolve the merge conflicts.

@bstrie
Copy link
Contributor

bstrie commented Jul 22, 2022

That seems likely to be a persistent source of confusion, since format_args is in the root.

Rather, it just means that we should be exposing format_args from std::fmt as well. The exposure of macros from the crate root is a legacy technical limitation that we are no longer beholden to, so let's not perpetuate the mistake now that it is no longer required.

I'm not seeing how this solves the problem? If format_args is exposed from both the crate root and std::fmt, then full consistency would require that this macro also be exposed from both. Otherwise, the set of places they're accessible from would be inconsistent, and could potentially cause confusion.

My nod towards consistency is that, if people want to be consistent, then expose both of these from std::fmt and discourge using the one version that's already exposed from the crate root. If we want to favor consistency as a philosophy, the fact that we expose so many random macros from the crate root is already responsible for a large amount of inconsistency; nothing else in std is defined directly in the crate root, and anything that's effectively-in-the-crate-root via the prelude has to be heavily justified, whereas for macros it's just a grab bag of random and niche functionality. I'm not even trying to advocate for any schemes for reducing the burden of having so many macros in the crate root, I'm just trying to keep the problem of inconsistency from getting any worse by adding new macros there. We have the capability these days to make macros consistent with every other item in std, so I'd like to use it.

@liigo
Copy link
Contributor

liigo commented Jul 25, 2022

I suggest providing the new functionality in syntax of

format_args!(ln, ...) and format_args!(nl, ...)

and maybe format_args!(newline, ...) or whatever.

and then there is no controversy about its naming and namespace, just stay with format_args.

I don't think it's a widely used macro, so a little more complex syntax doesn't matter IMO.

@est31
Copy link
Member

est31 commented Aug 27, 2022

👋 Hello, I'm writing this comment in this stabilization PR to notify you, the authors of this PR, that #100591 has been merged, which implemented a change in how features are stabilized.

Your PR has been filed before the change, so will likely require modifications in order to comply with the new rules. I recommend you to:

  1. rebase the PR onto latest master, so that uses of the placeholder are possible.
  2. replace the version numbers in the PR with the placeholder CURRENT_RUSTC_VERSION. For language changes, this means the version numbers in accepted.rs (example: 4caedba). For library changes, this means the since fields (example e576a9b).

That's it! The CURRENT_RUSTC_VERSION placeholder will, as part of the release process, be replaced with the version number that the PR merged for. It can be used anywhere in rust-lang/rust, not just accepted.rs and the since fields.

If you have any questions, feel free to drop by the zulip stream, or ping me directly in this PR's thread. Thanks! 👋

@joshtriplett
Copy link
Member Author

I would love to revisit this.

It seems like there are two categories of concerns here.

First, there are the various bikesheddings of what this should be named. I really don't have a strong opinion here, apart from wanting consistency.

Second, there's the question of whether to have this at all.

On that point, it feels like this is being held to a standard that, for instance, println/eprintln/writeln aren't held to. Why do we have writeln!(w, "{}{}", a1, a2), when someone could just do write!(w, "{}\n", format_args!("{}{}", a1, a2)) instead? Because writeln is useful and common and we provide a convenient way to invoke it.

I would argue that even if we have a version of format_args! that doesn't add overhead when nested, there's still value in providing format_args_ln! as a shorthand.

@schultetwin1
Copy link

I would argue that even if we have a version of format_args! that doesn't add overhead when nested, there's still value in providing format_args_ln! as a shorthand.

I'd like to add a specific +1 to this. When developing in a no_std environment, developers have to create their own print! and println! macros. We don't need format_args_ln! to do this, but it's useful to have.

@joshtriplett
Copy link
Member Author

So, since this was last discussed, @m-ou-se wrote #106824 which has been merged.

With that merged, I think we don't need this anymore, and even the standard library doesn't need to have it. Yes, it's convenient, but it's no longer a necessity for performance reasons, given that PR.

@schultetwin1
Copy link

Gotcha, thanks for the pointer Josh!

@m-ou-se
Copy link
Member

m-ou-se commented May 1, 2023

#111060 shows that format_args_nl!(..) now has neither a runtime nor compile time advantage over format_args!("{}\n", format_args!(..)) which I think is pretty cool.

@joshtriplett joshtriplett deleted the stabilize-format-args-nl branch June 23, 2023 12:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs-fcp This change is insta-stable, so needs a completed FCP to proceed. relnotes Marks issues that should be documented in the release notes of the next release. S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.