W Rust makra pozwalają na generowanie kodu na etapie kompilacji, zapewniając potężne narzędzia do metaprogramowania, zmniejszania boilerplate oraz realizacji DSL. Główne rodzaje makr:
macro_rules!.#[derive], makra atrybutowe (#[some_macro]) i podobne do funkcji (custom_macro!()).Deklaratywne są prostsze w użyciu dla kodu szablonowego, proceduralne dają większą kontrolę nad składnią i analizą tokenów.
Przykład deklaratywnego makra:
macro_rules! vec_of_strings { ($($x:expr),*) => { { let mut v = Vec::new(); $(v.push($x.to_string());)* v } }; } let v = vec_of_strings!("a", "b"); // => Vec<String>
Przykład makra proceduralnego (przykład derive):
#[derive(Debug, Clone)] struct MyStruct; // samo derive Debug jest zaimplementowane jako makro proceduralne w std.
Czy makra w Rust mogą generować składniowo niepoprawny kod lub kod z błędami w czasie wykonywania?
Odpowiedź: Tak, makra nie sprawdzają poprawności rozwinięcia w momencie pisania; błędy mogą ujawniać się dopiero po podstawieniu makra przez kompilator. Deklaratywne makra mogą prowadzić do nieoczywistych błędów składniowych. Makra proceduralne mogą generować niepoprawny lub podatny na błędy kod, dlatego krytycznie ważne jest dokładne testowanie ich działania.
Przykład:
macro_rules! make_error { () => { let x = ; // błąd składniowy wystąpi podczas używania makra } }
Historia
W dużym projekcie w celu redukcji boilerplate użyto macro_rules!, nie pokrywając wszystkich przypadków wzorów. Użytkownik przypadkowo przekazał do makra nieobsługiwane wyrażenie, co doprowadziło do nieczytelnego błędu kompilacji, którego przyczynę trudno było śledzić.
Historia
Podczas przenoszenia makr proceduralnych między crate wystąpiły problemy z niekompatybilnością wersji TokenStream API, przez co IDE zawieszało się, a błąd ujawniał się tylko w kompilacjach no_std.
Historia
Podczas pisania DSL do konfiguracji z makrem proceduralnym zaimplementowano niebezpieczną analizę wejściowych tokenów (bez walidacji typów), co prowadziło do dziwnych błędów w czasie wykonywania, podatności oraz niemożności poprawnego wdrożenia części nowej funkcjonalności.