Formatln! = format! + platform specific newline

At the moment it's not so easy to add a platform specific newline to a string. It's easy in c# ( https://docs.microsoft.com/en-us/dotnet/api/system.environment.newline ) or java ( System (Java Platform SE 8 ) ) but not in standard rust. At the moment println! is the only way to achieve it out of the box.

How's about we have a formatln! that's just like format! but with a platform specific newline on the end of the created string?

Even for a batteries not included std lib this is something I'd expect to see included (just like we have rust path separator ( std::path::MAIN_SEPARATOR - Rust ).

(Maybe I'm missing some super easy way to get a platform specific line ending in std rust?)

println! does not have any platform-specific behavior. It uses "\n" as the line ending on all platforms.

In addition, standard Rust tools like rustc and cargo use only "\n" line endings in both terminal output and in files they write, on all platforms.

The idea of “platform-specific newline” is somewhat obsolete, as evidenced by the fact that your existing Rust code works without it even though you assumed it didn’t. (Even Notepad.exe has supported "\n" line endings for years now.)

15 Likes

\r is dead? Well that's great to hear. That certainly makes things simpler.

1 Like

C stdio APIs have the requirement that \n in files opened in text mode be transparently translated to the platform line ending, in output as well as in input.

Not sure if this only applies to C, or indeed, typical non- \n platforms do this for all text streams, but I suspect it's the latter, and it just works in Rust for that reason.

It’s a bit more long winded but you can use writeln!.

let mut s = String::new();
let _ = writeln!(&mut s, "{}", 5);

FWIW, I appreciate that Rust just uses \n here. I'm happy to put the \r myself if needed.

Having to deal with TextWriter.NewLine in C# has caused issues for me just as much as it's helped.

1 Like

Having things silently normalize-out OS specific newlines (where "out" is another part of your program, not the OS) is asking for OS portability issues.

I fixed a Windows test failure for some crate I was using that was due to my checkout having CRLF. The solution the maintainer used there was to just normalize to \n on reading the file (as it was a snapshot test iirc, test the program gives the same output as in the file). The same way that include_str! and multiline string literals normalize CRLF to just LF.

Automatically normalizing-in can be surprising, but it leads to code just working in more places, being more platform (and git auto.crlf) agnostic to where it was compiled.

Oh, and a fun place where auto CRLF messed things up: reading an exe from a QR code (for fun). Because of normalize-out (and in one place forgetting to set binary mode), \n bytes in the exe were being "corrected" to \r\n.

1 Like

Since when does include_str! normalize line endings? There was a PR in FCP a while back that I successfully argued against. If the behavior has changed, that is a serious issue.

Just checked, it doesn't. rustc 1.49.0 (e1884a8e3 2020-12-29)

fn main() {
	let stuff = include_bytes!("crlf.txt");
	println!("{:?}", stuff);
}
// Output: [99, 114, 108, 102, 13, 10, 99, 114, 108, 102, 13, 10]
//                             ^^^^^^                     ^^^^^^

Edit: Oh, oops, include_bytes!() wasn't the question. Same result with include_str!() and .as_bytes() though.

1 Like

Hm, I could've sworn include_str! (not include_bytes!) did. I guess I confused it with a literal source string, which definitely does normalize "raw" CRLF (i.e. ones physically present in the .rs file) to just LF (but not explicit \r\n ofc).