Intro
What is Strut?
Strut refers to the strut crate.
It’s main job is to simplify common backend chores:
- Startup:
- Load externalized configuration.
- Build asynchronous runtime.
- Initialize logging/tracing.
- Set up integrations with external systems.
- Spindown:
- Release held resources.
- Close external connections.
What’s included by default
The strut crate has many features, but none of them are enabled by default.
At the baseline, these well-known crates are always pulled in by Strut:
config: to load the application config from files & environment.tokio: to build the asynchronous runtime for the application.serde: to conveniently deserialize the external config.dotenvy: to support environment defaults from.envfiles.- Implementation details, such as
parking_lot.
Yes, Strut requires an asynchronous runtime, and makes an opinionated choice for Tokio.
This comes from the fact that almost all Strut components have some sort of async play in them.
No, there is no option to switch to alternatives such as smol.
Tokio is a robust, well-maintained, and widely adopted runtime.
Additionally, the kind of applications that Strut aims at are not expected to bottleneck their performance on runtime choice.
Strut is not designed for bare-metal applications, a.k.a. no-std, or embedded systems without an OS.
Instead, Strut targets the “classical” backend applications: web/API servers, macro-/microservices, data processors, etc., where the binary size is not expected to be a constraining factor.
Install Strut
Adding Strut to a binary crate is as simple as:
- Add Strut dependency with
cargo add strut. - Prefix the
#[strut::main]attribute to the main function, and make itasync.
#[strut::main]
async fn main() {
// ...
}
It’s ok to replace the #[tokio::main] attribute with #[strut::main].
The latter wraps and includes the former.
If the Tokio runtime needs to be customized, this is possible using a custom RuntimeWiring.
Launchpad
The #[strut::main] attribute is designed for simplistic use-cases.
It is equivalent to:
fn main() {
let body = async {
// ...
};
strut::App::boot(body);
}
The call to the App::boot method is itself equivalent to:
fn main() {
let body = async {
// ...
};
strut::Launchpad::new(body).boot();
}
The Launchpad object exposes a number of pre-boot customizations.
If you want to customize how Strut boots the application, Launchpad should be the first thing to look at.
Next steps
The base Strut dependency spins up a Tokio runtime and loads the application configuration.
Without any components, the configuration is practically empty.
Adding a component, such as tracing is the logical next step.
For crates and use-cases not yet baked into Strut, custom integrations are possible.