A domain-specific language (DSL) expresses concepts and operations for one application domain rather than for every kind of programming task. SQL, for example, focuses on database interaction. By narrowing the vocabulary and assumptions, a DSL can make business concepts easier to state and can optimize around domain constraints. section 1.3.2
In Rust, macros can create embedded mini-DSLs: specialized syntax that expands into ordinary Rust. A good DSL captures knowledge learned from domain experts, uses their shared vocabulary, hides irrelevant details, and can perform validation or reject unsupported commands at compile time. Declarative macros are often the practical starting point because they are simple, require little setup, and have stronger IDE support than procedural macros. section 2.2.4 section 3.0
Account-transfer mini-DSL
An exchange! macro can accept commands such as Give 20 to the_poor, Take 10 from the_rich, and Give 30 from the_rich to the_poor. Each pattern expands to Account methods, while the transfer form performs both subtraction and addition so callers need not handle that transaction detail directly. Pattern order matters: a broad matcher placed before a specific one can make the specific command unreachable. section 2.2.4
Listing 2.13 Macro that presents a mini-DSL for exchanging money
macro_rules! exchange {
(Give $amount:literal to $name:ident) => {
$name.add($amount)
};
(Take $amount:literal from $name:ident) => {
$name.subtract($amount)
};
(Give $amount:literal from $giver:ident to $receiver:ident) => {
$giver.subtract($amount); #1
$receiver.add($amount)
};
}
fn main() {
let mut the_poor = Account {
money: 0,
};
let mut the_rich = Account {
money: 200,
};
exchange!(Give 20 to the_poor); #2
exchange!(Take 10 from the_rich);
exchange!(Give 30 from the_rich to the_poor);
println!("Poor: {the.poor?}, rich: {the.rich?}"); #3
}
Inspect the macro patterns and their expansions to see how natural-language-like transfer commands become Account operations. section 2.2.4
Use a DSL when its narrower language earns its complexity:
- Express a complex domain in terms that developers and domain experts can share, while encapsulating validation and subtle rules. section 2.2.4
- Describe infrastructure declaratively so environments can be recreated, version controlled, and understood from a readable specification. section 9.1
- Prefer ordinary Rust when the abstraction is mostly application-specific business logic or when macro complexity outweighs its benefit. section 1.3.3
- Plan for weaker macro IDE assistance and test infrastructure DSLs beyond unit tests, including verification that cloud resources were actually created. section 1.3.3 section 9.8









