0Pricing
Learn Rust Coding · Lekcja

Makra proceduralne: Derive, Function

Poznają Państwo makra proceduralne, w tym własne makra `#[derive]` i makra funkcyjne, służące do bardziej złożonego generowania kodu.

Makra proceduralne: Derive, Function to bezpłatna lekcja Learn Rust Coding na CoddyKit. To lekcja 2 z 3. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Learn Rust Coding, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Learn Rust Coding zawiera 3 lekcji w sumie.

Wprowadzenie do makr proceduralnych

Witamy w świecie makr proceduralnych! W przeciwieństwie do makr deklaratywnych (macro_rules!) makra proceduralne przypominają funkcje operujące bezpośrednio na kodzie Rust.

Przyjmują jako dane wejściowe TokenStream, przetwarzają je i zwracają kolejny TokenStream. Umożliwia to znacznie bardziej złożone generowanie kodu.

Rodzaje makr proceduralnych

Rust oferuje trzy główne rodzaje makr proceduralnych:

  • Makra funkcyjne: Wyglądają i działają jak zwykłe wywołania funkcji, np. my_macro!(...).
  • Makra derive: Umożliwiają automatyczne implementowanie traitów dla struktur i enumeracji za pomocą #[derive(MyTrait)].
  • Makra atrybutów: Umożliwiają definiowanie niestandardowych atrybutów, które można stosować do elementów, np. #[route("/path")].

Crate `proc-macro`

Makra proceduralne muszą znajdować się w osobnym, specjalnym rodzaju crate'a. W pliku Cargo.toml deklaruje się go w następujący sposób:

Informuje to Cargo, że crate zawiera makra przekształcające kod w czasie kompilacji.

[package]
name = "my_macros"
version = "0.1.0"
edition = "2021"

[lib]
proc-macro = true

Makra funkcyjne — wyjaśnienie

Makra funkcyjne definiuje się za pomocą atrybutu #[proc_macro] umieszczonego przy funkcji, która przyjmuje proc_macro::TokenStream i zwraca taki sam typ.

Są przydatne do tworzenia niestandardowych DSL-i (języków dziedzinowych) lub złożonego powtarzania kodu, z którym macro_rules! nie potrafi sobie poradzić.

use proc_macro::TokenStream;

#[proc_macro]
pub fn my_function_macro(input: TokenStream) -> TokenStream {
    // Logic to transform input TokenStream
    // ...
    input // For example, return the input unchanged
}

Przykład makra funkcyjnego

Oto przykład poglądowy. Makro `greet_name!` generuje instrukcję wypisywania. Rzeczywista implementacja analizowałaby wejściowy TokenStream, aby wyodrębnić nazwę.

Choć definicja makra jest złożona, jego użycie jest proste i daje duże możliwości:

/* In 'my_macros/src/lib.rs' */
use proc_macro::TokenStream;

#[proc_macro]
pub fn greet_name(input: TokenStream) -> TokenStream {
    // Imagine parsing 'input' to get a name like 'World'
    // and generating: println!("Hello, World!");
    "println!(\"Hello, {}!\", \"CoddyKit\");".parse().unwrap()
}

/* In 'my_app/src/main.rs' */
use my_macros::greet_name;

fn main() {
    greet_name!("CoddyKit");
}

Zrozumienie makr `#[derive]`

Makra derive są najczęściej używanym rodzajem makr. Umożliwiają automatyczne implementowanie traitu dla struktury lub enumeracji.

Po zapisaniu #[derive(Debug)] makro uruchamia się w czasie kompilacji i generuje kod niezbędny do zaimplementowania przez dany typ traitu Debug.

Implementowanie `#[derive]` (koncepcja)

Niestandardowe makra derive zazwyczaj tworzy się z użyciem bibliotek takich jak syn (do analizowania kodu Rust i przekształcania go w AST) oraz quote (do generowania kodu Rust na podstawie AST).

Należy zdefiniować funkcję opatrzoną adnotacją #[proc_macro_derive(MyTrait)], która przyjmuje TokenStream reprezentujący strukturę lub enumerację i zwraca wygenerowaną implementację traitu.

/* In 'my_macros/src/lib.rs' */
use proc_macro::TokenStream;
use syn::{parse_macro_input, DeriveInput};
use quote::quote;

#[proc_macro_derive(MyDebug)] // MyDebug is the trait name
pub fn my_debug_derive(input: TokenStream) -> TokenStream {
    let ast = parse_macro_input!(input as DeriveInput);
    let name = &ast.ident;

    let expanded = quote! {
        impl std::fmt::Debug for #name {
            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                f.debug_struct(stringify!(#name)).finish()
            }
        }
    };
    expanded.into()
}

Przykład użycia `#[derive(Debug)]`

Przyjrzyjmy się praktycznemu przykładowi makra derive, którego prawdopodobnie już Państwo używali: Debug. Dodając #[derive(Debug)] do struktury Point, Rust automatycznie implementuje trait Debug, co pozwala wygodnie wyświetlać jej zawartość.

#[derive(Debug)]
struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let p = Point { x: 10, y: 20 };
    println!("My point is: {:?}", p);
}

Makra atrybutów: `#[my_attribute]`

Makra atrybutów przypominają makra derive, ale można je stosować do dowolnych elementów (funkcji, struktur, modułów itd.). Przyjmują jako dane wejściowe element, do którego zostały dołączone, oraz wszelkie argumenty umieszczone w nawiasach.

Typowe zastosowania obejmują routing we frameworkach internetowych (np. #[get("/users")]) oraz konfigurację testów (np. #[test]).

/* In 'my_macros/src/lib.rs' */
use proc_macro::TokenStream;

#[proc_macro_attribute]
pub fn log_calls(attr: TokenStream, item: TokenStream) -> TokenStream {
    // Imagine adding println! statements to 'item'
    item
}

/* In 'my_app/src/main.rs' */
use my_macros::log_calls;

#[log_calls("entry_point")]
fn my_function() {
    println!("Inside my_function");
}

fn main() {
    my_function();
}

Kiedy używać poszczególnych makr

  • Makra funkcyjne: Do tworzenia niestandardowych minijęzyków lub złożonych przekształceń dowolnych bloków kodu.
  • Makra derive: Do automatycznego implementowania traitów dla struktur i enumeracji.
  • Makra atrybutów: Do modyfikowania kodu lub generowania kodu wokół określonych elementów (funkcji, struktur) na podstawie niestandardowych atrybutów.

Każdy rodzaj służy innemu celowi, rozszerzając składnię i możliwości języka Rust.

Szybkie sprawdzenie: rodzaje makr

Które z poniższych stwierdzeń dotyczących makr proceduralnych są PRAWDZIWE?

Podsumowanie: potęga generowania kodu

Poznali Państwo zaawansowany świat makr proceduralnych!

  • Poznaliśmy makra funkcyjne do tworzenia niestandardowej składni.
  • Zrozumieliśmy, jak makra #[derive] automatyzują implementowanie traitów.
  • Krótko omówiliśmy makra atrybutów do generowania kodu na poziomie elementów.

Makra proceduralne to potężne narzędzie do ograniczania powtarzalnego kodu i rozszerzania możliwości Rust, które pozwala pisać kod generujący kod!

Często zadawane pytania

Czy lekcja „Makra proceduralne: Derive, Function” jest bezpłatna?

Tak — pełny tekst „Makra proceduralne: Derive, Function” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Learn Rust Coding, przejdź na CoddyKit PRO. Kurs Learn Rust Coding zawiera 3 lekcji w sumie.

Co nauczysz się w „Makra proceduralne: Derive, Function”?

Poznają Państwo makra proceduralne, w tym własne makra `#[derive]` i makra funkcyjne, służące do bardziej złożonego generowania kodu. Ćwiczysz Learn Rust Coding z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Learn Rust Coding?

Nie wymagamy żadnego doświadczenia. Learn Rust Coding w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 2 z 3.

Ile czasu zajmuje lekcja „Makra proceduralne: Derive, Function”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Learn Rust Coding?

Tak. Każda lekcja Learn Rust Coding zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Makra deklaratywne (`macro_rules!`)
  2. Makra proceduralne: Derive, Function
  3. Praca z Rust w trybie unsafe
← Powrót do Learn Rust Coding