syn and quote
- Rust course
- Advanced
- Lesson 51/81
- 4 min
Generate code with the syn and quote crates.
syn and quote
The two basic tools for writing procedural macros:
syn: parses aTokenStreaminto a Rust syntax tree (DeriveInput,ItemFn,Expr...).quote: turns Rust code written insidequote! { .. }, with#variableinterpolations, back into aTokenStream.
let ad = &ast.ident; // syn::Ident: Kisi
let n = alanlar.len();
let uretilen = quote! {
impl #ad {
fn alan_sayisi() -> usize { #n }
}
};In the intermediate step the code isn't text but structured tokens; this lets error messages point to the right source location.
Seeing code generation as text
We can see what quote! produces by building the same template with text; in macro debugging, cargo expand shows exactly this:
fn impl_uret(ad: &str, alanlar: &[&str]) -> String {
format!(
"impl {ad} {{ fn alan_sayisi() -> usize {{ {} }} }}",
alanlar.len()
)
}Real macros use quote! instead of string concatenation; the text approach is open to quoting, escaping and location problems.
Practice Code from a template
Write the impl_uret function above. Print the result of impl_uret("Kisi", &["ad", "yas"]).
Starter code
fn main() {
// buraya yaz
}Sample solution
fn impl_uret(ad: &str, alanlar: &[&str]) -> String {
format!(
"impl {ad} {{ fn alan_sayisi() -> usize {{ {} }} }}",
alanlar.len()
)
}
fn main() {
println!("{}", impl_uret("Kisi", &["ad", "yas"]));
}Expected output
impl Kisi { fn alan_sayisi() -> usize { 2 } }
Questions and answers
Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.
1 How should a user error in a macro (e.g. a struct macro applied to an enum) be reported?
- A compile error at the right location with syn::Error::new_spanned(..).to_compile_error() Correct answer
- panic!
- Silently empty output
- println!
Explanation: A panic also gives a compile error but with a vague location; an error with a span takes the user to the right line.
2 Interpolate a variable
quote! {
impl ____ad {
fn alan_sayisi() -> usize { #n }
}
}
quote! {
impl #ad {
fn alan_sayisi() -> usize { #n }
}
}
Explanation: Inside quote!, #ad interpolates the ad variable into the code.
3 Tool and output
| Term | Meaning |
|---|---|
syn::parse_macro_input! |
Syntax tree |
quote! |
TokenStream |
cargo expand |
Expanded code |
syn::Error |
Error with location |