How Does Rustlings Embed Official Exercises? A Deep Dive into Compile-Time Bundling
Rustlings embeds its official exercises using a compile-time procedural macro (rustlings_macros::include_files!) that reads info.toml and generates include_bytes! calls for every exercise file, solution, and README, storing them in a static EmbeddedFiles struct that is written to disk when users run rustlings init.
The rust-lang/rustlings repository distributes a self-contained CLI tool that teaches Rust through hands-on exercises. Rather than requiring users to download exercise files separately or clone the entire repository, Rustlings bakes all 100+ exercises directly into the compiled binary. This article explains the mechanical process of how the project achieves this embedding through procedural macros and static byte arrays.
The Compile-Time Embedding Pipeline
The embedding mechanism operates in two distinct phases: a procedural macro that runs during compilation to bundle files, and a static data structure that holds those files for runtime extraction.
Stage 1: The include_files! Procedural Macro
Located in [rustlings-macros/src/lib.rs](https://github.com/rust-lang/rustlings/blob/main/rustlings-macros/src/lib.rs), the include_files! macro executes entirely at build time. It performs the following operations:
- Loads the manifest: Reads the repository's
info.tomlusinginclude_bytes!("../info.toml"), explicitly filtering out carriage return characters (\r) to normalize line endings across platforms. - Parses exercise metadata: Deserializes the TOML content to extract exercise names and directory structures.
- Generates inclusion tokens: For each exercise, constructs file paths (e.g.,
../exercises/intro/intro1.rs,../solutions/intro/intro1.rs) and emitsinclude_bytes!macro calls wrapped in a static initialization.
The macro expands into code resembling this pattern:
static EMBEDDED_FILES: EmbeddedFiles = EmbeddedFiles {
info_file: "...", // Parsed TOML content as string
exercise_files: &[
ExerciseFiles {
exercise: include_bytes!("../exercises/intro/intro1.rs"),
solution: include_bytes!("../solutions/intro/intro1.rs"),
dir_ind: 0,
},
// ... additional exercises
],
exercise_dirs: &[
ExerciseDir {
name: "intro",
readme: include_bytes!("../exercises/intro/README.md"),
},
// ... additional directories
],
};
Stage 2: The EmbeddedFiles Static Structure
The data structures that store these embedded bytes are defined in [src/embedded.rs](https://github.com/rust-lang/rustlings/blob/main/src/embedded.rs). The three core types work together to organize the embedded content:
EmbeddedFiles: The top-level container holding theinfo.tomlstring, an array ofExerciseFiles, and an array ofExerciseDirentries.ExerciseFiles: Contains three fields—exercise(the source bytes),solution(the solution bytes), anddir_ind(an index pointing to the directory metadata).ExerciseDir: Stores the directory name and its associatedREADME.mdbytes.
By using include_bytes!, Rust stores the exact file contents as static byte slices (&'static [u8]) within the binary's read-only data segment, ensuring zero runtime I/O overhead until extraction is requested.
Runtime Extraction and Initialization
When a user executes rustlings init, the application invokes the extraction logic to recreate the exercise tree on the local filesystem.
The init_exercises_dir Method
As implemented in [src/embedded.rs](https://github.com/rust-lang/rustlings/blob/main/src/embedded.rs), the init_exercises_dir method handles the physical write operations:
- Creates the root directory: Generates the
exercises/folder and writes the top-levelREADME.md. - Reconstructs subdirectories: Iterates through
exercise_dirsto create category folders (e.g.,exercises/intro/,exercises/error_handling/) and writes their respective README files. - Writes exercise files: Matches exercise metadata from
info.tomlwith the embedded byte arrays inexercise_files, reconstructing paths likeexercises/intro/intro1.rsand writing the bytes usingfs::write.
This method is invoked from [src/init.rs](https://github.com/rust-lang/rustlings/blob/main/src/init.rs#L124-L136) during the initialization command:
// From src/init.rs
EMBEDDED_FILES.init_exercises_dir(&exercises)?;
The extraction process treats the embedded bytes as immutable templates, ensuring that every user receives the identical, official exercise set regardless of their platform or network connectivity.
Why Compile-Time Embedding Matters
This architecture provides several distinct advantages for a CLI learning tool:
- Self-contained distribution: The single binary artifact contains everything needed to bootstrap the learning environment, eliminating dependency on Git submodules or network fetching.
- Version consistency: The exercises are permanently bound to the specific binary version; a Rustlings v6.0.0 binary always contains the v6.0.0 exercise set, preventing drift between tool and content.
- Cross-platform integrity: By filtering line endings during macro execution and using byte-level embedding, the tool ensures that exercise files are written with the host platform's native line endings regardless of where the binary was compiled.
Summary
rustlings_macros::include_files!inrustlings-macros/src/lib.rsreadsinfo.tomlat compile time and generatesinclude_bytes!calls for every exercise source, solution, and documentation file.EmbeddedFiles, defined insrc/embedded.rs, stores all embedded content as static byte arrays organized by directory and exercise type.- The
init_exercises_dirmethod extracts these embedded bytes to the filesystem, recreating the completeexercises/hierarchy when users runrustlings init. src/init.rsorchestrates the extraction process by calling the initialization method on the static singleton.- This compile-time embedding strategy ensures Rustlings remains a portable, offline-capable tool with guaranteed version alignment between the CLI and its exercise curriculum.
Frequently Asked Questions
Does Rustlings require an internet connection to download exercises?
No. Because all exercises are embedded directly into the binary using include_bytes!, Rustlings operates entirely offline after installation. When you run rustlings init, the CLI simply writes the pre-bundled files from the EmbeddedFiles static struct to your local filesystem without making any network requests.
What file determines which exercises get embedded into the binary?
The [info.toml](https://github.com/rust-lang/rustlings/blob/main/info.toml) file in the repository root serves as the manifest. The include_files! macro parses this TOML file during the build process to determine the directory structure and relative paths for every exercise, solution, and README that must be embedded.
How does the embedding mechanism handle Windows vs. Unix line endings?
The macro in [rustlings-macros/src/lib.rs](https://github.com/rust-lang/rustlings/blob/main/rustlings-macros/src/lib.rs) explicitly filters out carriage return characters (\r) when reading info.toml by iterating through the include_bytes! result and removing them before UTF-8 conversion. This ensures consistent TOML parsing across all platforms during compilation, while the embedded exercise files are stored verbatim and written exactly as they exist in the source tree.
Can I modify the embedded exercises and have Rustlings use my customized versions?
No, the embedded files are immutable and fixed at compile time. However, once you run rustlings init, the embedded bytes are written to your working directory as regular source files, which you are intended to edit to complete the exercises. If you delete your local exercises/ folder and run init again, the original embedded versions from the binary will be restored.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →