421: `include!`, `include_str!`, `include_bytes!`
Tutorial Video
Text description (accessibility)
This video demonstrates the "421: `include!`, `include_str!`, `include_bytes!`" functional Rust example. Difficulty level: Fundamental. Key concepts covered: Functional Programming. Binary assets (images, shaders, SQL queries, configuration files) can be embedded directly into executables. Key difference from OCaml: 1. **Built
Tutorial
The Problem
Binary assets (images, shaders, SQL queries, configuration files) can be embedded directly into executables. Instead of loading them from disk at runtime (which can fail if the file is missing or the path changes), include_bytes! and include_str! embed the file content as a compile-time constant. The resulting binary is self-contained: no external files needed, no file-not-found errors at runtime. include! includes arbitrary Rust source files, enabling code generation workflows where a build.rs produces Rust code that is then include!'d.
These macros are used by rust-embed, WASM binary bundlers, shader compilers, and any application embedding resources (game assets, localization strings, TLS certificates).
🎯 Learning Outcomes
include_bytes! embeds a file as &'static [u8] at compile timeinclude_str! embeds a UTF-8 file as &'static strinclude! includes generated Rust source code from build.rs workflowsprintln!("cargo:rerun-if-changed=...")Code Example
#![allow(clippy::all)]
//! include! and include_str! Macros
//!
//! Including files at compile time.
/// Include raw bytes from a file.
pub const README_BYTES: &[u8] = include_bytes!("../Cargo.toml");
/// Example SQL query.
pub fn example_query() -> &'static str {
"SELECT * FROM users"
}
/// Include at compile time.
#[macro_export]
macro_rules! include_sql {
($name:literal) => {
concat!("-- Query: ", $name, "\n", "SELECT * FROM ", $name)
};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_include_bytes() {
assert!(!README_BYTES.is_empty());
}
#[test]
fn test_include_sql() {
let sql = include_sql!("users");
assert!(sql.contains("users"));
}
#[test]
fn test_example_query() {
assert!(example_query().contains("SELECT"));
}
#[test]
fn test_cargo_toml_content() {
let s = std::str::from_utf8(README_BYTES).unwrap();
assert!(s.contains("[package]"));
}
#[test]
fn test_file_macro() {
let f = file!();
assert!(f.contains("lib.rs"));
}
}Key Differences
include_bytes! and include_str! are language primitives; OCaml requires build system configuration or external libraries.(deps ...) in dune rules.include! includes arbitrary Rust code; OCaml's load_path and dynamic loading serve similar purposes but differently.OCaml Approach
OCaml achieves file embedding through the dune build system's (embed_file ...) rule or through the sedlex and menhir grammar inclusion patterns. The Crunch library converts files into OCaml modules. For inline SQL, ppx_rapper generates type-safe queries from SQL strings in OCaml source. OCaml has no built-in equivalent of include_bytes! — file embedding requires build system configuration.
Full Source
#![allow(clippy::all)]
//! include! and include_str! Macros
//!
//! Including files at compile time.
/// Include raw bytes from a file.
pub const README_BYTES: &[u8] = include_bytes!("../Cargo.toml");
/// Example SQL query.
pub fn example_query() -> &'static str {
"SELECT * FROM users"
}
/// Include at compile time.
#[macro_export]
macro_rules! include_sql {
($name:literal) => {
concat!("-- Query: ", $name, "\n", "SELECT * FROM ", $name)
};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_include_bytes() {
assert!(!README_BYTES.is_empty());
}
#[test]
fn test_include_sql() {
let sql = include_sql!("users");
assert!(sql.contains("users"));
}
#[test]
fn test_example_query() {
assert!(example_query().contains("SELECT"));
}
#[test]
fn test_cargo_toml_content() {
let s = std::str::from_utf8(README_BYTES).unwrap();
assert!(s.contains("[package]"));
}
#[test]
fn test_file_macro() {
let f = file!();
assert!(f.contains("lib.rs"));
}
}#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_include_bytes() {
assert!(!README_BYTES.is_empty());
}
#[test]
fn test_include_sql() {
let sql = include_sql!("users");
assert!(sql.contains("users"));
}
#[test]
fn test_example_query() {
assert!(example_query().contains("SELECT"));
}
#[test]
fn test_cargo_toml_content() {
let s = std::str::from_utf8(README_BYTES).unwrap();
assert!(s.contains("[package]"));
}
#[test]
fn test_file_macro() {
let f = file!();
assert!(f.contains("lib.rs"));
}
}
Deep Comparison
OCaml vs Rust: macro include
See example.rs and example.ml for side-by-side implementations.
Key Points
Exercises
include_str! to embed a TOML config file into your binary. Parse it at startup using the toml crate and expose configuration values as constants. Show that the binary works without the config file being present at runtime.include_str! constants. Create a ShaderSource { vert: &'static str, frag: &'static str } and verify the strings contain valid GLSL keywords.build.rs that reads a data/keywords.txt file and generates src/generated.rs containing pub const KEYWORDS: &[&str] = &[...];. Use include!("../src/generated.rs") to include the generated code.