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

[RFC] Allow for crates to define their own fmt or disable it #247

Closed
sajattack opened this issue Nov 4, 2018 · 7 comments
Closed

[RFC] Allow for crates to define their own fmt or disable it #247

sajattack opened this issue Nov 4, 2018 · 7 comments

Comments

@sajattack
Copy link

Similar to the panic_fmt of old, which allowed us to provide our own implementations of what to do when panicking, I think we should be able to have our own formatters, and especially to be able to provide an empty fmt, for crates like alloc that use fmt::Display, fmt::Debug, everywhere. Most of the time we have nowhere to display anything so having this huge dependency is a waste.

@sajattack sajattack changed the title [RFC] Allow for crates to define their own fmt/disable it [RFC] Allow for crates to define their own fmt or disable it Nov 4, 2018
@japaric
Copy link
Member

japaric commented Dec 10, 2018

As written I don't think this would work in practice.

and especially to be able to provide an empty fmt

This breaks functionality. write!(file, ..) (e.g. to a file in a SD card) and write!(socket, ..) (e.g. to a UDP socket) are reasonable things to do in embedded code. If you set the formatting behavior to no-op then you break the functionality of those operations, which may be used in your dependencies, and your application will no longer behave as expected.

Similar to the panic_fmt of old

The panic handler is a terminator and most applications won't rely on it having a specific behavior for their functionality (with the exception of the test crate which relies on panic=unwind). This means that most libraries will not link to a panic handler and that the top crate (the application) is free to pick the panic handler. That's not the case for formatting behavior, which is rather problematic. Consider these two scenarios:

  1. Imagine I write a networking library that needs to use the write! macro. I need formatting to work (not be a no-op) so I link to the fmt-std crate, which provides the functionality currently provided by core::fmt. Because there can only be a single fmt_handler in the dependency graph all crates that link to my networking library can no longer pick a different formatting strategy.

  2. Imagine Alice writes a FAT filesystem libraries that uses the write! macro. She needs formatting to work but not something as featureful as fmt-std so she adds fmt-fast as a dependency to her crate. Again, because there can only be a single fmt_handler in the dependency graph now no one can use my networking library together with Alice's FAT filesystem library.

cc @nagisa who probably have some thoughts about ^


crates like alloc that use fmt::Display, fmt::Debug, everywhere

Why is alloc a problem and not core? core contains fmt::Debug and fmt::Display implementations for (almost) all the types defined in it as well as for all primitives. Most of these won't make it to the final binary because the linker will discard everything not referenced by the application.

Most of the time we have nowhere to display anything so having this huge dependency is a waste.

If you are not using core::fmt at all, and that includes the panic handler (e.g. panic-halt or panic-abort), then there should be no core::fmt stuff in your final binary. Is that not the case? Are we talking about unoptimized code here?

@sajattack
Copy link
Author

sajattack commented Dec 10, 2018

I was compiling in release mode and my binary wound up with core::fmt, the only place I could figure it was coming from was core::fmt::Debug/Display in alloc::boxed::Box ¯\_(ツ)_/¯

@jonas-schievink
Copy link
Contributor

@japaric
Copy link
Member

japaric commented Dec 10, 2018

@sajattack you may want to run cargo-call-stack on your program to see what's calling the fmt stuff.

@jonas-schievink that sounds more reasonable.

I personally keep hearing this "trait objects make your code small / inlining bloats your code" mantra even though all my no_std experience indicates the contrary so I would like to see a Cargo.toml option along the lines of panic={abort,unwind} to replace the current core::fmt stuff with a fully (or mostly) inline-able version that has the same features. Then we can check if that produces smaller no_std binaries or not.

@sajattack
Copy link
Author

@japaric I get this error with cargo call-stack

thread 'main' panicked at 'bug? llvm.memset.p0i8.i32 does not appear to be a valid symbol', /home/paul/.cargo/registry/src/jackfan.us.kg-1ecc6299db9ec823/cargo-call-stack-0.1.0/src/main.rs:299:25

@japaric
Copy link
Member

japaric commented Dec 10, 2018

@sajattack please file a bug report in the issue tracker

@Dirbaio
Copy link
Member

Dirbaio commented Jun 11, 2024

Closing this as part of 2024 triage.

This is not actionable by the Embedded WG. Only upstream Rust has the power to accept the proposed changes (make the core::fmt implementation pluggable).

A few notes about fmt bloat:

  • core::fmt does get optimized out if you don't use it.
  • One source of "using" it involuntarily is the panic handler. Even if the panic handler doesn't print the panic message, rustc is unable to optimize out everything. The nightly-only fix is using panic-immediate-abort, see Making panic-never a requirement or convention for rust-embedded libraries where feasible #551 (comment). No equivalent stable fix is available yet.
  • The Rust sdlib recently got a optimize_for_size feature. This means rust-lang/rust is now accepting PRs that use different smaller algorithms/datastructures when it's enabled to reduce bloat. This is a way we sould reduce the size of core::fmt for embedded.

@Dirbaio Dirbaio closed this as completed Jun 11, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants