# Feature request: std::fmt::Write::write\_line

**URL:** <https://internals.rust-lang.org/t/feature-request-std-write-line/14751>\
**Category:** libs\
**Created:** [May 21, 2021, 3:25pm UTC](https://internals.rust-lang.org/t/feature-request-std-write-line/14751 "2021-05-21T15:25:45Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![Soni](https://avatars.discourse-cdn.com/v4/letter/s/a3d4f5/32.png) [@Soni](https://internals.rust-lang.org/u/Soni)\
**Post date:** [May 21, 2021, 3:25pm UTC](https://internals.rust-lang.org/t/feature-request-std-write-line/14751/1 "2021-05-21T15:25:45Z")

</div>

`hexchat-plugin` has a `print(impl ToString)` which is a line-writer. It doesn't support the equivalent of `write_fmt`. It'd be nice if `write_fmt` and `write_line` were separate so that our `write_fmt` could be `unimplemented!()` and `write_line` didn't push a newline at the end (altho hexchat doesn't care about a terminating newline, and in fact slightly prefers it, but w/e).

This would be used by `writeln!`.

---

<div class="post-metadata">

**Author:** ![Nemo157](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/nemo157/32/11585_2.png) [@Nemo157](https://internals.rust-lang.org/u/Nemo157)\
**Post date:** [May 21, 2021, 5:14pm UTC](https://internals.rust-lang.org/t/feature-request-std-write-line/14751/2 "2021-05-21T17:14:39Z")

</div>

You can use something like a `LineBuffer` that flushes on newlines to support such a writer.

---

<div class="post-metadata">

**Author:** ![Soni](https://avatars.discourse-cdn.com/v4/letter/s/a3d4f5/32.png) [@Soni](https://internals.rust-lang.org/u/Soni)\
**Post date:** [May 21, 2021, 6:00pm UTC](https://internals.rust-lang.org/t/feature-request-std-write-line/14751/3 "2021-05-21T18:00:25Z")

</div>

std::fmt::Write is supposed to have a write\_str but it'd be nice to have that be unimplemented!() and also have write\_fmt be unimplemented!() and also have write\_char be unimplemented!() and instead have write\_line implemented.

this is the source code for the crate, consider taking a look at it: [lib.rs - source](https://docs.rs/hexchat-plugin/0.2.14/src/hexchat_plugin/lib.rs.html) we had an argument about this very issue on the community discord, also note that the default write\_fmt calls write\_str multiple times which is extremely less-than-ideal for this use-case and we're also not sure if there's any guarantee that write\_fmt doesn't get called multiple times.

---

<div class="post-metadata">

**Author:** ![Soni](https://avatars.discourse-cdn.com/v4/letter/s/a3d4f5/32.png) [@Soni](https://internals.rust-lang.org/u/Soni)\
**Post date:** [June 22, 2021, 2:04pm UTC](https://internals.rust-lang.org/t/feature-request-std-write-line/14751/4 "2021-06-22T14:04:10Z")

</div>

Any opposition to making `writeln!` use a new `write_line` method?

---

<div class="post-metadata">

**Author:** ![bjorn3](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/bjorn3/32/2736_2.png) [@bjorn3](https://internals.rust-lang.org/u/bjorn3)\
**Post date:** [June 22, 2021, 4:46pm UTC](https://internals.rust-lang.org/t/feature-request-std-write-line/14751/5 "2021-06-22T16:46:30Z")

</div>

That would break anyone expecting `writeln!()` to call `.write_fmt()`.

---

<div class="post-metadata">

**Author:** ![Soni](https://avatars.discourse-cdn.com/v4/letter/s/a3d4f5/32.png) [@Soni](https://internals.rust-lang.org/u/Soni)\
**Post date:** [June 23, 2021, 12:10am UTC](https://internals.rust-lang.org/t/feature-request-std-write-line/14751/6 "2021-06-23T00:10:09Z")

</div>

write\_line would call write\_fmt.

---

<div class="post-metadata">

**Author:** ![skysch](https://avatars.discourse-cdn.com/v4/letter/s/f05b48/32.png) [@skysch](https://internals.rust-lang.org/u/skysch)\
**Post date:** [June 23, 2021, 2:40am UTC](https://internals.rust-lang.org/t/feature-request-std-write-line/14751/7 "2021-06-23T02:40:56Z")

</div>

How would that work? `write_fmt` takes `std::fmt::Arguments`. How would `writeln!` pass them to `write_fmt` if it is calling `write_line`?

Why not just implement write\_fmt and ignore the args yourself? (Or use another trait that doesn't have unimplemented methods that will panic at the drop of a hat?)

---

<div class="post-metadata">

**Author:** ![Soni](https://avatars.discourse-cdn.com/v4/letter/s/a3d4f5/32.png) [@Soni](https://internals.rust-lang.org/u/Soni)\
**Post date:** [June 23, 2021, 2:46am UTC](https://internals.rust-lang.org/t/feature-request-std-write-line/14751/8 "2021-06-23T02:46:54Z")

</div>

write\_line takes fmt::Arguments, and calls write\_fmt with another fmt::Arguments.

```rust
trait Write {
  ...
  fn write_line(&mut self, f: std::fmt::Arguments<'_>) -> Result<...> {
    self.write_fmt(format_args!("{}\n", f)) // or something
  }
}

```

The whole point is to be able to do

```rust
impl Write for Foo {
  fn write_fmt(...) -> ... { unimplemented!() }
  fn write_line(...) -> ... { actually does stuff }
}

```

---

<div class="post-metadata">

**Author:** ![skysch](https://avatars.discourse-cdn.com/v4/letter/s/f05b48/32.png) [@skysch](https://internals.rust-lang.org/u/skysch)\
**Post date:** [June 23, 2021, 2:48am UTC](https://internals.rust-lang.org/t/feature-request-std-write-line/14751/9 "2021-06-23T02:48:42Z")

</div>

How does that help you?

> [@Soni](#):
>
> It doesn't support the equivalent of `write_fmt`

Doesn't that make `write_line` just as useless to you?

It sounds like you want `writeln!` to not write its newline, but for `write!` to just be broken? What happens if someone puts in a newline manually?

---

<div class="post-metadata">

**Author:** ![bjorn3](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/bjorn3/32/2736_2.png) [@bjorn3](https://internals.rust-lang.org/u/bjorn3)\
**Post date:** [June 23, 2021, 7:44am UTC](https://internals.rust-lang.org/t/feature-request-std-write-line/14751/10 "2021-06-23T07:44:34Z")

</div>

What if you use `writeln!` on the following type:

```rust
struct Foo:

impl Foo {
    fn write_fmt(&mut self, fmt: Arguments) {}
}

```

This type doesn't have a `write_line` method, so if `writeln!` were to be changed to use `write_line`, it would break on this type.

---

<div class="post-metadata">

**Author:** ![Soni](https://avatars.discourse-cdn.com/v4/letter/s/a3d4f5/32.png) [@Soni](https://internals.rust-lang.org/u/Soni)\
**Post date:** [June 23, 2021, 11:15am UTC](https://internals.rust-lang.org/t/feature-request-std-write-line/14751/11 "2021-06-23T11:15:41Z")

</div>

writeln! is for std::fmt::Write and std::io::Write not for your Foo.

---

<div class="post-metadata">

**Author:** ![Soni](https://avatars.discourse-cdn.com/v4/letter/s/a3d4f5/32.png) [@Soni](https://internals.rust-lang.org/u/Soni)\
**Post date:** [June 23, 2021, 11:16am UTC](https://internals.rust-lang.org/t/feature-request-std-write-line/14751/12 "2021-06-23T11:16:56Z")

</div>

The point is that it's a line writer. It doesn't support non-line writes. And indeed, it'll accept embedded newlines but they'll behave slightly different from "lines".

But the main issue is that write\_fmt doesn't work for this.

---

<div class="post-metadata">

**Author:** ![skysch](https://avatars.discourse-cdn.com/v4/letter/s/f05b48/32.png) [@skysch](https://internals.rust-lang.org/u/skysch)\
**Post date:** [June 23, 2021, 4:01pm UTC](https://internals.rust-lang.org/t/feature-request-std-write-line/14751/13 "2021-06-23T16:01:02Z")

</div>

> [@Soni](#):
>
> `fn write_line(&mut self, f: std::fmt::Arguments<'_>) -> Result<...>`

This has the _exact same function signature_ of `write_fmt`. This is why I don't understand why `write_fmt` "doesn't work". You can make it do whatever you need it to do. You can defer to other traits. You can have other traits defer to it. You can wrap writers in constructs that ensure specific behaviors. Changing the function's name doesn't _do_ anything.

Moreover, you're suggesting that you want every existing method on `Write` to be `unimplemented!`. This is a huge flag indicating that this is not the trait you want to use.

These discussions are meant for us to find the best solutions to problems, not to ram through half-baked designs just because you insist your problem space is so inflexible that only what you propose will work. There are many ways to solve your problem. I'm trying to figure out what those _are_, and it's not helpful when you keep insisting this is 'against the whole point' of what you're trying to do. That makes it sound like your whole point is to win an argument.

---

<div class="post-metadata">

**Author:** ![Soni](https://avatars.discourse-cdn.com/v4/letter/s/a3d4f5/32.png) [@Soni](https://internals.rust-lang.org/u/Soni)\
**Post date:** [June 23, 2021, 4:14pm UTC](https://internals.rust-lang.org/t/feature-request-std-write-line/14751/14 "2021-06-23T16:14:45Z")

</div>

write\_fmt doesn't write lines.

writeln writes lines.

writeln currently uses write\_fmt to write lines, but that's not what we want. our concept of line isn't based on newline characters in the content. writeln is what everyone is used to for writing lines tho, so it makes sense to use it.

---

<div class="post-metadata">

**Author:** ![skysch](https://avatars.discourse-cdn.com/v4/letter/s/f05b48/32.png) [@skysch](https://internals.rust-lang.org/u/skysch)\
**Post date:** [June 23, 2021, 4:20pm UTC](https://internals.rust-lang.org/t/feature-request-std-write-line/14751/15 "2021-06-23T16:20:32Z")

</div>

[`writeln!`](https://doc.rust-lang.org/stable/std/macro.writeln.html):

"Write formatted data into a buffer, with a newline appended. On all platforms, the newline is the LINE FEED character ( `\n` / `U+000A` ) alone (no additional CARRIAGE RETURN ( `\r` / `U+000D` )."

It doesn't make sense for `writeln!` to do something other than what it is documented to do. If you have some other concept of a line, that should be implemented at a lower level, like `std::io::write`.

---

<div class="post-metadata">

**Author:** ![bjorn3](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/bjorn3/32/2736_2.png) [@bjorn3](https://internals.rust-lang.org/u/bjorn3)\
**Post date:** [June 23, 2021, 7:53pm UTC](https://internals.rust-lang.org/t/feature-request-std-write-line/14751/16 "2021-06-23T19:53:07Z")

</div>

> [@Soni](#):
>
> writeln! is for std::fmt::Write and std::io::Write not for your Foo.

Nope, it is actively being depended on. Eg [nushell](https://github.com/nushell/nushell/blob/104cf5b51b39dce9d4fb67232b0b45f1b457da91/crates/nu-ansi-term/src/write.rs#L8), [RustPython](https://github.com/RustPython/RustPython/blob/bee5794b6e2b777ee343c7277954b73d06b5cb7d/vm/src/py_io.rs#L10) and [materialize](https://github.com/MaterializeInc/materialize/blob/613ffedbe4188fc9afbd080a3ea0bf8ddf552561/src/ore/src/fmt.rs#L78). Changing this is a breaking change and as such not allowed without a very good reason. A very good reason would pretty much only be an important bug fix. A new feature like this is not a good reason. You can write your own `writeln!()` if you want this feature for yourself, but the libstd `writeln!()` is unlikely to change.

---

<div class="post-metadata">

**Author:** ![Soni](https://avatars.discourse-cdn.com/v4/letter/s/a3d4f5/32.png) [@Soni](https://internals.rust-lang.org/u/Soni)\
**Post date:** [June 24, 2021, 2:58am UTC](https://internals.rust-lang.org/t/feature-request-std-write-line/14751/17 "2021-06-24T02:58:42Z")

</div>

That is somewhat concerning, but honestly, just...

write! and writeln! have to be macros because of formatting, and they're also the _idiomatic_ way to format stuff into an object. unfortunately not all objects can work with the semantics demanded by write! and writeln!, and then ergonomics kiiinda go out the window because you have to use thing.print(format\_args!(...)) which is so much more verbose and has more nested parentheses than writeln!(thing, ...). also there's currently no guarantee that writeln! calls write! (and thus write\_fmt) only once, so you can't rely on _that_ either.

for the hexchat-plugin crate, you already have multiple levels of indentation due to all the stuff about relying on the stack/lifetimes to provide memory safety, so lines like ph.print(format\_args!(...)) _really_ eat your horizontal space. being able to writeln!(ph, ...) would solve that, and the only way we can think of to make that acceptable to ppl is if there was such a fmt::Writer::write\_line or something. (nobody has addressed this yet...)

write\_line just makes more sense, from a more conceptual perspective, than what we have today. it has all sorts of advantages over what we have today.

(also macros are scoped/namespaced now. we could have a v2 write/writeln that isn't part of the prelude.)

---

<div class="post-metadata">

**Author:** ![CAD97](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cad97/32/3460_2.png) [@CAD97](https://internals.rust-lang.org/u/CAD97)\
**Post date:** [June 24, 2021, 5:12am UTC](https://internals.rust-lang.org/t/feature-request-std-write-line/14751/18 "2021-06-24T05:12:17Z")

</div>

> [@Soni](#):
>
> also there's currently no guarantee that writeln! calls write! (and thus write\_fmt) only once, so you can't rely on _that_ either

So why not ask for that? It makes total sense to specifically promise that (especially since it falls under observable behavior and the "it just does `w.write_fmt`" that is relied upon and (mostly) guaranteed. This would be a simple docs PR with an FCP, then merged, probably resolved faster than this thread, and definitely faster than any extensions to the `Write` interface.

If this is guaranteed (which, depending on consensus, it might already effectively be!), then you have two clean options to interface with your sink:

- Have users use `write!` for each unit chunk pushed to your sink. I think this is fair enough, especially if you want to preserve internal newlines.
- Have users use `writeln!`, and buffer writes, which you then pass along to your log backend after splitting on newlines.

It's not like `fmt::Arguments` gives you any introspection power anyway, so you eventually have to dump it into a string (or another write sink, I suppose, which probably still internally uses a string buffer for a format call _anyway_) one way or another. It barely costs you anything to then check for a trailing newline then (what a `write_fmt_line` would eliminate checking). Even scanning and splitting on newline characters is probably dwarfed in cost by the actual formatting and writing.

* * *

There are existing ways to accomplish what you've communicated you want, that work quite well already. What you're proposing is that the language is changed impacting everyone else to marginally improve (if at all) one edge case you're dealing with, and insisting it be done the way you are proposing it should be done, and just dismissing proposed alternatives as missing the point.

I hope you can see how this negatively reflects back on the proposals you put forward. You may not intend to be, but it reads as standoffish (or even bad faith, depending on how (un)charitable the reader is feeling) on our side of the screen, as we lack the context that you have.

* * *

And just in general, I also would like to see the formatting machinery more thoroughly documented and specified. Some `Write` sinks should probably guarantee being zero-(extra)-copy, in that they don't use an internal buffer and just `write` each fragment written by `fmt` calls. Maybe more sinks should provide an inherent `write_fmt` so you don't _have_ to import `Write` to write to them. Perhaps the implementation could be improved or made more configurable, or more could be exposed to the consumer so they have more ways to implement `write_fmt` than just dumping to another sink. The most meaningful small improvement I can think of would actually be documenting if `fmt_arguments.fmt(w)` is guaranteed to be equivalent to `w.write_fmt(fmt_arguments)` (modulo requisite (re)borrowing), or just that it writes the same content.

But all of this can (and should) be done without breaking the public API.

---

<div class="post-metadata">

**Author:** ![Soni](https://avatars.discourse-cdn.com/v4/letter/s/a3d4f5/32.png) [@Soni](https://internals.rust-lang.org/u/Soni)\
**Post date:** [June 24, 2021, 1:10pm UTC](https://internals.rust-lang.org/t/feature-request-std-write-line/14751/19 "2021-06-24T13:10:10Z")

</div>

It'd still be good to have a new API for "line" writers. Well maybe "paragraph" writers would be more accurate. But sure, make it a guarantee that write\_fmt is only called once.

---

<div class="post-metadata">

**Author:** ![system](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/system/32/14092_2.png) [@system](https://internals.rust-lang.org/u/system)\
**Post date:** [September 22, 2021, 1:10pm UTC](https://internals.rust-lang.org/t/feature-request-std-write-line/14751/20 "2021-09-22T13:10:35Z")

</div>

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.
