Module guide
A Guide to the Rustmax crate.
Should you use rustmax as a crate?
Definitely not.
Firstly, it's completely unstable and unsupported and always has breaking changes.
I dogfood the rustmax crate in many projects,
and I have found the benefits of using it directly minimal,
though I do prefer using it to starting from scratch every project.
The primary benefit is simply having the crate names for common solutions directly available
without re-searching crates.io.
To that end I do find it convenient to start a new project by initing
the rustmax template with the portable profile and going from there —
all my crates are in one place, and I know they will at least build on WASM,
a decent proxy for "portable".
It's also a single place to handle common crate upgrades and cargo audit resolutions,
which is a nice benefit when you have multiple projects using rustmax.
But there are significant downside still:
- Build and link time. They aren't unusual for a large Rust project,
but you do pay to build a lot of code you definitely aren't using.
Even after the initial build you continue to pay the linkage cost.
The obvious solution, the experimental
mostly-unusedhint that is applied to the rustmax crate (on nightly), so far hasn't produced significant speedups. - Re-exported proc macros need help. Most are handled by writing
#[rmx::derive(..)]instead of#[derive(..)], butthiserrorandcxxstill require a direct dependency. See Macros. - Disk usage. Lots of deps to download and upgrade and build and rebuild
even for tiny projects. I run
cargo cleanandcargo clean-alla lot.
Getting started
Add rustmax to your Cargo.toml with a profile enabled:
[dependencies]
rmx.package = "rustmax"
rmx.version = "0.0.10"
rmx.features = [
"rmx-profile-portable",
]
Or if using a workspace, define the dependency in your workspace Cargo.toml:
[workspace.dependencies]
rmx.package = "rustmax"
rmx.version = "0.0.10"
rmx.features = [
"rmx-profile-portable",
]
Then in each crate's Cargo.toml:
[dependencies]
rmx.workspace = true
In your own code then you can access
common crates through the rmx path.
# use rustmax as rmx;
use *;
let config: Value =
from_str?;
let port = config.as_u64.ok_or?;
assert_eq!;
# Ok::
The crates of Rustmax
| Category | Crates |
|---|---|
| error handling and debugging | anyhow, env_logger, log, thiserror |
| collections | ahash, bitflags, bytes, itertools |
| numerics | libm, num_bigint |
| encoding, serialization, parsing | base64, comrak, flate2, hex, json5, memchr, nom, regex, serde, serde_json, tar, toml, zip |
| time | chrono, jiff |
| random numbers | rand, rand_chacha, rand_pcg |
| cryptography | blake3, sha2 |
| parallelism | crossbeam, rayon |
| asynchronous I/O | futures, tokio |
| networking and web | axum, http, hyper, mime, reqwest, socket2, tera, tower, url |
| text / unicode | unicode_segmentation |
| convenience macros | cfg-if, derive_more, extension-trait, num_enum, powerletters |
| terminal / CLI | clap, ctrlc, indicatif, termcolor, rustyline |
| system / OS | glob, ignore, notify, tempfile, walkdir, xshell |
| graphics / images | image |
| testing | proptest |
| FFI / interop | libc, bindgen, cc, cxx, cxx-build |
| build scripts | ... |
| deployment and software lifecycle | semver |
| procedural macros | proc-macro2, quote, syn |
A Guide to the Rustmax crate
- Cargo features and profiles
- Crate reexports
- Standard library reexports
- The
rustmaxprelude - The
extrasmodule - Starting from a template
- Macros
- Known bugs
- Profiles.
rustmaxorganizes crates into profiles, which correspond to common target environments and application types. - Ecosystem features.
rustmaxidentifies Cargo features common across many crates. - Rust standard libraries.
rustmaxre-exports the standard Rust libraries for convenience. rustmaxcrate API docs
Cargo features and profiles
rustmax enables no features by default and reexports no crates.
All configuration is done through Cargo features,
primarily through profile features.
Profiles select sets of crates for common use cases:
rmx-profile-portable is a good default for most applications,
rmx-profile-std adds crates that require native OS features,
and rmx-profile-net adds networking and async I/O.
See Profiles for the full list.
In addition to profiles,
ecosystem features provide cross-cutting control
over individual crate features like derive, serde, and tokio support.
Profiles enable sensible defaults for these,
but they can also be toggled individually.
See Ecosystem features for details.
Crate reexports
All rustmax crates are reexported as modules:
# use rustmax as rmx;
use Rng;
These modules behave the same as the corresponding crates,
with exceptions noted in Macros.
Each module has rustmax-specific documentation
with a description, example, and links to the original crate docs.
Modules are only defined when their crate is enabled
through a profile feature like rmx-profile-portable.
Standard library reexports
rustmax also reexports the Rust standard libraries as modules,
enabled automatically by profiles.
See Rust standard libraries.
The rustmax prelude
The extras module
Starting from a template
Macros
Macros are the one part of rustmax that does not
simply work the way the original crate's documentation says it does.
This section explains why, and what to write instead.
The short version: use rmx::derive
in place of #[derive(..)].
# use rustmax as rmx;
Why re-exported macros are different
A derive macro has to generate code that refers back to its own crate.
serde's derive generates calls into serde,
clap's derive generates calls into clap.
The macro cannot know what the user calls that crate,
so it hardcodes the crate's real name into its output.
That name is resolved in the crate the macro was used in,
where it usually resolves against the extern prelude --
the set of direct Cargo dependencies.
A crate that depends on rustmax has rustmax in its extern prelude,
not serde, so the generated code does not compile.
Nothing about this affects ordinary re-exported items.
Types, traits, functions and macro_rules! macros
all work through rustmax exactly as they do directly,
because macro_rules! macros refer to their own crate
through $crate, which survives re-export.
Only proc macros -- derive macros and attribute macros -- are affected.
The affected crates behave in three distinct ways, which is why there is no single workaround:
| Crate | What its generated code names | Reachable through rustmax? |
|---|---|---|
serde |
extern crate serde |
yes, via #[serde(crate = "..")] |
num_enum |
::num_enum |
yes, via #[num_enum(crate = ..)] |
clap |
clap, relative to the use site |
yes, by binding the name in scope |
derive_more |
derive_more, relative to the use site |
yes, by binding the name in scope |
thiserror |
::thiserror |
no |
cxx |
::cxx |
no |
A leading :: resolves only against the extern prelude,
so for thiserror and cxx no re-export, import, or macro
can make the generated code resolve.
Those two need a direct dependency.
#[rmx::derive(..)]
rmx::derive is an attribute macro
that stands in for #[derive(..)].
It rewrites each derive to its path within rustmax
and adds whatever that particular derive crate needs
in order to find its runtime.
# use rustmax as rmx;
let json = to_string?;
assert_eq!;
# Ok::
Container and field attributes work unchanged,
and names it does not recognize are passed straight through,
so built-in derives can be listed alongside re-exported ones.
It knows the derives of
clap, derive_more, num_enum, serde and thiserror.
Where a derive name is also a built-in derive --
Debug, Eq and PartialEq all exist in derive_more --
the built-in one wins.
Qualify the name to ask for the other:
# use rustmax as rmx;
assert_eq!;
Error is the reverse case:
both thiserror and derive_more provide it,
and a bare Error means thiserror's,
because that is what it usually means elsewhere.
Since thiserror needs a direct dependency,
#[rmx::derive(Error)] without one is an error
that says so and points at the alternative.
Adding a direct dependency on one of these crates is always allowed,
and #[rmx::derive] notices and uses it,
generating exactly what the crate's own documentation describes.
Mixing the two styles in one crate is fine.
Importing macros without #[rmx::derive]
#[rmx::derive] is not the only way,
and for clap, derive_more and tokio
the plain import is barely different from upstream.
These crates name themselves relative to the use site,
so binding the name is enough:
# use rustmax as rmx;
use ;
assert_eq!;
The self in that import is the whole trick:
it binds clap as well as Parser,
and the generated code needs the former.
Importing only rmx::clap::Parser fails to compile.
The same applies to attribute macros,
which #[rmx::derive] does not cover:
# use rustmax as rmx;
use tokio;
async
For serde, the equivalent is its crate attribute,
which takes the path to use in generated code:
# use rustmax as rmx;
use ;
Note that the path there is the real crate name.
If the dependency is renamed, as the template renames it to rmx,
the attribute has to say "::rmx::serde" instead.
#[rmx::derive] exists partly to avoid having to know that:
it discovers the name from the depending crate's manifest.
Adding a direct dependency
For thiserror and cxx this is the only option,
and it is a reasonable one for any of the affected crates.
Add the crate alongside rustmax:
[dependencies]
rmx.package = "rustmax"
rmx.version = "0.0.10"
rmx.features = ["rmx-profile-portable"]
thiserror = "2"
This costs less than it looks like it does.
Cargo unifies the two references to a single build of the crate,
so nothing is compiled twice,
and rmx::serde::Serialize and serde::Serialize
are the same trait, not two versions of it.
What the extra line buys is a name in the extern prelude.
The cost is that the version is now declared in two places,
which is exactly what rustmax exists to avoid.
Keep the version ranges loose enough
that Cargo can unify them with whatever rustmax selects.
Known limitations
#[rmx::derive] reads the depending crate's manifest
to decide whether a crate is a direct dependency.
The manifest does not distinguish [dependencies] from [dev-dependencies],
but the extern prelude of a given build target contains only one of them.
A crate listed under [dependencies] but not [dev-dependencies]
is treated as available inside integration tests, where it is not.
Adding it to both sections resolves this.
Writing #[serde(crate = "..")] on a type
that also has #[rmx::derive(Serialize)]
produces a duplicate-attribute error from serde.
Use one or the other.
Proc macro functions cannot be called from other proc macros,
so rustmax cannot provide drop-in replacements
for the derive macros themselves,
only an attribute that rewrites what is handed to them.
Cargo has no mechanism for a library crate
to add entries to its dependents' extern prelude;
the unstable public-dependency feature does not do this.
That is why thiserror and cxx have no workaround.
Known bugs
(None currently.)
Profiles
rustmax organizes crates into profiles,
which correspond to common target environments and application types.
By default no profile is enabled and no crates are exported.
Profile: rmx-profile-no-std
This profile includes crates that do not require Rust std.
It allows use of the Rust allocator,
and enables allocator-related features of its crates.
All crates in this profile are also in rmx-profile-std.
This profile also enables rmx-feature-no-std.
This profile also enables rmx-rustlib-core and rmx-rustlib-alloc.
Crates in rmx-profile-no-std
ahash- A fast and DOS-resistent hash function, for use inHashMaps.anyhow- Flexible error handling.base64- Base-64 encoding and decoding.bitflags- Types in which the bits are individually addressable.blake3- The BLAKE3 cryptographic hash function.bytes- Abstractions for working with byte buffers:Bytes,Buf, andBufMut.cfg-if- A macro for writing conditional compilation asif/elseblocks.chrono- Dates and time (legacy).crossbeam- Concurrency tools to supplementstd::sync, including fast channels.derive_more-derivefor more standard traits.extension-trait- A macro for defining extension methods to external types.futures- Abstractions for asynchronous programming.hex- Encoding and decoding hexadecimal strings.itertools- Additional methods for iterators.jiff- Dates and time.libc- Bindings to the C standard library.libm- Floating-point math functions, whichcoredoes not provide.log- A simple logging framework.memchr- Fast byte search with SIMD acceleration.nom- An efficient parser combinator.num_bigint- Arbitrary-sized integers.num_enum- Conversions between numbers and enums.powerletters- Superscript and subscript Unicode text conversion.rand- Random number generators.rand_chacha- The ChaCha cryptographically-secure random number generators.rand_pcg- The PCG non-cryptographically-secure random number generators.semver- The software versioning standard used by Rust.serde- The standard Rust serialization framework.serde_json- JSON serialization / deserialization withserde.sha2- The SHA2 cryptographic hash functions.toml- TOML serialization / deserialization withserde.
Profile: rmx-profile-std
This profile depends on the Rust standard library,
and includes crates that require the Rust standard library,
in addition to the crates provided by rmx-profile-no-std.
This profile also enables rmx-feature-std.
This profile also enables rmx-feature-default.
This profile also enables rmx-feature-more.
This profile also enables rmx-feature-derive.
This profile also enables rmx-feature-serde.
This profile also enables rmx-rustlib-core, rmx-rustlib-alloc, and rmx-rustlib-std.
Crates in rmx-profile-std
clap- Command line parsing.comrak- CommonMark and GitHub Flavored Markdown parser.env_logger- A basic logger to use with thelogcrate.flate2- Deflate, gzip, and zlib compression and decompression.glob- Unix shell style pattern matching for paths.ignore- Directory traversal respecting gitignore rules.image- Image processing and manipulation.indicatif- Progress bars and spinners for CLI.json5- JSON5, a superset of JSON with expanded syntax.notify- Cross-platform filesystem notifications.proptest- Testing over generated inputs, ala QuickCheck.rayon- Parallel iterators and other parallel processing tools.regex- Regular expressions.tar- Read and write tar archives; pair withflate2for.tar.gz.tempfile- Temporary files and directories.tera- A text template engine based on Jinja2.thiserror- Tools for defining custom error types.unicode-segmentation- Splitting strings on grapheme cluster, word, and sentence boundaries.walkdir- Efficient directory traversal.xshell- A Swiss-army knife for writing shell-style scripts in Rust.zip- Read and write ZIP archives.
Profile: rmx-profile-portable
This profile is designed for portable targets including WebAssembly (WASM)
and cross-compiled environments like Linux musl.
It includes all crates from rmx-profile-no-std,
plus additional crates that are compatible with these environments.
This profile uses portable variants of ecosystem features that exclude features incompatible with WASM or requiring a C cross-compiler toolchain, such as OS-specific threading APIs, file system operations that require native OS support, and C library dependencies like zstd.
This profile also enables rmx-rustlib-core, rmx-rustlib-alloc, and rmx-rustlib-std.
This profile also enables rmx-feature-std-portable.
This profile also enables rmx-feature-default-portable.
This profile also enables rmx-feature-more-portable.
This profile also enables rmx-feature-derive.
This profile also enables rmx-feature-serde.
Crates in rmx-profile-portable
All crates from rmx-profile-no-std, plus:
clap- Command line parsing.comrak- CommonMark and GitHub Flavored Markdown parser.env_logger- A basic logger to use with thelogcrate.flate2- Deflate, gzip, and zlib compression and decompression.glob- Unix shell style pattern matching for paths.json5- JSON5, a superset of JSON with expanded syntax.rayon- Parallel iterators and other parallel processing tools.regex- Regular expressions.tar- Read and write tar archives; pair withflate2for.tar.gz.tempfile- Temporary files and directories.thiserror- Tools for defining custom error types.unicode-segmentation- Splitting strings on grapheme cluster, word, and sentence boundaries.walkdir- Efficient directory traversal.zip- Read and write ZIP archives.
Profile: rmx-profile-net
Adds networking crates,
including the tokio async runtime.
Not that this profile does not enable tokio features
for other crates; to enable tokio features
apply the rmx-feature-tokio feature.
This profile also enables rmx-profile-std.
Crates in rmx-profile-net
axum- Web application framework based ontokio.http- Shared definitions related to the HTTP protocol.hyper- HTTP, versions 1 and 2.mime- MIME media types.reqwest- Simple HTTP requests, synchronous and asynchronous.socket2- Low-level network socket programming beyondstd::net.tokio- An async task runtime and I/O library.tower- Service request/response abstraction (HTTP middleware) fortokioandaxum.url- URL parsing and data structures.
Profile: rmx-profile-cli
Crates for building commandline interfaces.
This profile also enables rmx-profile-std.
Crates in rmx-profile-cli
ctrlc- Simple handling of Ctrl-C for CLI programs.termcolor- Cross-platform library for writing colored output to the terminal.rustyline- Command-line input reading with history.
Profile: rmx-profile-build-script
Crates for writing Rust build scripts.
This profile also enables rmx-profile-std.
Crates in rmx-profile-build-script
bindgen- Generate Rust bindings to C and C++ libraries.cc- A basic cross-platform C/C++ compiler driver.cxx- C++ bridge runtime support; paired withcxx_build.cxx-build- C++ bridge generator; paired withcxx.
Profile: rmx-profile-proc-macro
Crates for writing Rust procedural macros.
This profile also enables rmx-profile-std.
This profile also enables rmx-rustlib-proc_macro.
Crates in rmx-profile-proc-macro
proc-macro2- A preferred wrapper around the standardproc_macrocrate.quote- Thequote!macro for turning code blocks into source tokens.syn- A Rust parser used by procedural macros.
Profile: rmx-profile-full
This profile simply enables all previous profiles.
This profile also enables rmx-profile-std.
This profile also enables rmx-profile-net.
This profile also enables rmx-profile-cli.
This profile also enables rmx-profile-build-script.
This profile also enables rmx-profile-proc-macro.
Profile: rmx-profile-max
rustmax with all features (that don't require nightly).
This profile also enables rmx-profile-full.
This profile also enables rmx-feature-derive.
This profile also enables rmx-feature-serde.
This profile also enables rmx-feature-backtrace.
This profile also enables rmx-feature-tokio.
Profile: rmx-profile-max-nightly
rustmax with all features (including nightly).
This profile also enables rmx-profile-max.
This profile also enables rmx-feature-nightly.
Ecosystem features
rustmax identifies Cargo features common across many crates.
Feature: rmx-feature-no-std
This feature is enabled by rmx-profile-no-std.
It does not typically need to be set manually.
It enables few features, particularly enabling allocator support for no-std crates that can be compiled without.
Feature: rmx-feature-std
This feature is enabled by rmx-profile-std.
It does not typically need to be set manually.
It enables the "std" feature of crates and other default features that require the standard library.
Feature: rmx-feature-std-portable
This feature is enabled by rmx-profile-portable.
It does not typically need to be set manually.
Similar to rmx-feature-std, but excludes features
that are incompatible with portable targets (WASM, musl cross-compilation),
such as those requiring threading or OS-specific APIs.
Feature: rmx-feature-default
This feature is enabled by rmx-profile-std.
It does not typically need to be set manually.
It enables the "default" feature of crates.
Feature: rmx-feature-default-portable
This feature is enabled by rmx-profile-portable.
It does not typically need to be set manually.
Similar to rmx-feature-default, but uses portable
default features where necessary (e.g., excludes zstd from zip).
Feature: rmx-feature-more
This feature is enabled by rmx-profile-std.
It does not typically need to be set manually.
This activates extra crate features for convenience that the crates themselves do not typically activate by default.
Feature: rmx-feature-more-portable
This feature is enabled by rmx-profile-portable.
It does not typically need to be set manually.
Similar to rmx-feature-more, but excludes features
that are incompatible with portable targets,
such as blocking I/O and threading.
Feature: rmx-feature-derive
Enables derive macros of crates where it is optional, typically with a feature named "derive".
Feature: rmx-feature-serde
Enables serde support for crates where it is optional,
typically with a feature named "serde".
Feature: rmx-feature-backtrace
Enables backtrace support for crates where it is optional, typically with a feature named "backtrace".
This feature is necessary for backtrace support in anyhow.
This feature also enables rmx-feature-std.
Feature: rmx-feature-tokio
Enables tokio support for crates where it is optional,
typically with a feature named "tokio".
Feature: rmx-feature-nightly
Enables features that only compile with the Rust [nightly compiler], typically with a feature named "nightly".
Rust standard libraries
rustmax re-exports the standard Rust libraries for convenience.
These features enable reexports of the corresponding standard library crates
as modules within rustmax.
Rustlib: rmx-rustlib-core
Reexports the core library.
Enabled by rmx-profile-no-std and all profiles that include it.
Rustlib: rmx-rustlib-alloc
Reexports the alloc library.
Enabled by rmx-profile-no-std and all profiles that include it.
Rustlib: rmx-rustlib-std
Reexports the std library.
Enabled by rmx-profile-std and all profiles that include it.
Rustlib: rmx-rustlib-proc_macro
Reexports the proc_macro library.
Enabled by rmx-profile-proc-macro.