derive macro
- Rust course
- Advanced
- Lesson 80/81
- 7 min
Write and test a custom derive macro.
Final task: derive macro
Design the code your own #[derive(Tanimla)] macro would generate: a tanim() method that writes the struct's name and each field with Display. In a real macro the fields are read with syn and this is generated with quote!:
impl Kisi {
fn tanim(&self) -> String {
let mut s = String::from("Kisi { ");
s.push_str(&format!("ad: {}", self.ad));
s.push_str(", ");
s.push_str(&format!("yas: {}", self.yas));
s.push_str(" }");
s
}
}In the exercise we'll generate the same code with a macro_rules! taking the struct definition.
Generation with macro_rules!
The repetition pattern produces one piece per field; join places the separators:
macro_rules! tanimlanir {
(struct $ad:ident { $($alan:ident : $tur:ty),* $(,)? }) => {
struct $ad { $($alan: $tur),* }
impl $ad {
fn tanim(&self) -> String {
let parcalar: Vec<String> = vec![
$(format!("{}: {}", stringify!($alan), self.$alan)),*
];
format!("{} {{ {} }}", stringify!($ad), parcalar.join(", "))
}
}
};
}self.$alan generates a field access with the macro variable.
Practice Describable struct
Write the tanimlanir! macro above and generate the definition struct Kisi { ad: String, yas: u32 } with it. Print the result of Kisi { ad: "Ece".into(), yas: 30 }.tanim().
Starter code
fn main() {
// buraya yaz
}Sample solution
macro_rules! tanimlanir {
(struct $ad:ident { $($alan:ident : $tur:ty),* $(,)? }) => {
struct $ad {
$($alan: $tur),*
}
impl $ad {
fn tanim(&self) -> String {
let parcalar: Vec<String> = vec![
$(format!("{}: {}", stringify!($alan), self.$alan)),*
];
format!("{} {{ {} }}", stringify!($ad), parcalar.join(", "))
}
}
};
}
tanimlanir!(struct Kisi { ad: String, yas: u32 });
fn main() {
let k = Kisi { ad: "Ece".into(), yas: 30 };
println!("{}", k.tanim());
}Expected output
Kisi { ad: Ece, yas: 30 }
Questions and answers
Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.
1 What is the easiest way to place the commas between fields correctly?
- Write a comma after each field and delete the last
- Not using commas
- It isn't possible in a macro
- Collect the pieces into a Vec<String> and join them with join(", ") Correct answer
Explanation: join solves the separator problem correctly, including the zero and single-element cases.
2 Generate field access
$(format!("{}: {}", stringify!($alan), self.____)),*
$(format!("{}: {}", stringify!($alan), self.$alan)),*
Explanation: self.$alan generates a separate access for each field.
3 Piece and role
| Term | Meaning |
|---|---|
stringify!($ad) |
Struct name |
stringify!($alan) |
Field name |
self.$alan |
Field value |
join(", ") |
Separators |