Lær at programmere i Rust · Lektion

Procedurelle makroer: derive og funktioner

Dyk ned i procedurelle makroer, herunder brugerdefinerede `#[derive]`-makroer og funktionslignende makroer, til mere kompleks kodegenerering.

Lektion 2 af 312 trin

Procedurelle makroer: derive og funktioner er en gratis Lær at programmere i Rust-lektion på CoddyKit. Dette er lektion 2 af 3. Du kan læse alle 3 lektioner i dette læringsspor gratis i deres fulde længde — derefter låser CoddyKit PRO alle lektioner op samt praktiske øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Den er en del af læringsforløbet i Lær at programmere i Rust, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Lær at programmere i Rust-kurset indeholder 3 lektioner i alt.

Introduktion til proceduremakroer

Velkommen til proceduremakroernes verden! I modsætning til deklarative makroer (macro_rules!) minder proceduremakroer om funktioner, der arbejder direkte på Rust-kode.

De tager en TokenStream som input, behandler den og returnerer en anden TokenStream. Det giver mulighed for langt mere kompleks kodegenerering.

Typer af proceduremakroer

Rust har tre hovedtyper af proceduremakroer:

  • Funktionslignende makroer: De ser ud og fungerer som almindelige funktionskald, f.eks. my_macro!(...).
  • Derive-makroer: De gør det muligt automatisk at implementere traits for structs og enums ved hjælp af #[derive(MyTrait)].
  • Attributmakroer: De gør det muligt at definere brugerdefinerede attributter, som kan anvendes på elementer, f.eks. #[route("/path")].

Crate'en `proc-macro`

Proceduremakroer skal ligge i deres egen særlige crate-type. I din Cargo.toml erklærer du den sådan:

Det fortæller Cargo, at crate'en indeholder makroer, som omformer kode på kompileringstidspunktet.

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

[lib]
proc-macro = true

Forklaring af funktionslignende makroer

Funktionslignende makroer defineres med attributten #[proc_macro] på en funktion, der tager en proc_macro::TokenStream og returnerer en.

De er nyttige til brugerdefinerede DSL'er (domænespecifikke sprog) eller kompleks kodegentagelse, som macro_rules! ikke kan håndtere.

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
}

Eksempel på en funktionslignende makro

Her er et konceptuelt eksempel. En makro `greet_name!`, der genererer en udskriftsinstruktion. Den faktiske implementering ville analysere inputtet TokenStream for at udtrække navnet.

Selvom makrodefinitionen er kompleks, er brugen enkel og effektiv:

/* 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");
}

Forståelse af `#[derive]`-makroer

Derive-makroer er den mest almindelige type. De gør det muligt automatisk at implementere et trait for en struct eller enum.

Når du skriver #[derive(Debug)], kører en makro på kompileringstidspunktet for at generere den nødvendige kode, så din type implementerer Debug-traitet.

Implementering af en `#[derive]` (koncept)

Brugerdefinerede derive-makroer bygges typisk ved hjælp af biblioteker som syn (til at analysere Rust-kode til et AST) og quote (til at generere Rust-kode fra et AST).

Du definerer en funktion med annoteringen #[proc_macro_derive(MyTrait)], som tager en TokenStream, der repræsenterer structen eller enum'en, og returnerer den genererede trait-implementering.

/* 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()
}

Eksempel på brug af `#[derive(Debug)]`

Lad os se på et praktisk eksempel på en derive-makro, som du sandsynligvis allerede har brugt: Debug. Ved at tilføje #[derive(Debug)] til vores Point-struct implementerer Rust automatisk Debug-traitet, så vi kan udskrive dens indhold på en overskuelig måde.

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

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

Attributmakroer: `#[my_attribute]`

Attributmakroer minder om derive-makroer, men kan anvendes på ethvert element (funktioner, structs, moduler osv.). De tager det element, de er knyttet til, samt eventuelle argumenter i parentes, som input.

Almindelige anvendelser omfatter routing i webframeworks (f.eks. #[get("/users")]) eller opsætning af test (f.eks. #[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();
}

Hvornår skal du bruge hvilken makro

  • Funktionslignende makroer: Til brugerdefinerede minisprog eller komplekse omdannelser af vilkårlige kodeblokke.
  • Derive-makroer: Til automatisk implementering af traits på structs og enums.
  • Attributmakroer: Til at ændre eller generere kode omkring bestemte elementer (funktioner, structs) baseret på brugerdefinerede attributter.

Hver type har sit eget formål, når Rusts syntaks og funktionalitet skal udvides.

Hurtigt tjek: Makrotyper

Hvilke af følgende udsagn om proceduremakroer er sande?

Opsummering: Kodegenereringens muligheder

Du har udforsket den avancerede verden af proceduremakroer!

  • Vi har lært om funktionslignende makroer til brugerdefineret syntaks.
  • Vi har forstået, hvordan #[derive]-makroer automatiserer trait-implementeringer.
  • Vi har kort set på attributmakroer til kodegenerering på elementniveau.

Proceduremakroer er et effektivt værktøj til at reducere boilerplate og udvide Rusts funktionalitet, så du kan skrive kode, der skriver kode!

Gratis at komme i gang

Lær Rust med en AI-underviser — gratis

Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.

Kurser
39
Lektioner
144

Ofte stillede spørgsmål

Er lektionen “Procedurelle makroer: derive og funktioner” gratis?

Ja — alle 3 lektioner i læringssporet Lær at programmere i Rust, inklusive “Procedurelle makroer: derive og funktioner”, kan læses gratis i deres fulde længde her på webstedet. Derefter låser CoddyKit PRO alle lektioner op samt interaktive øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Lær at programmere i Rust-kurset indeholder 3 lektioner i alt.

Hvad lærer jeg i “Procedurelle makroer: derive og funktioner”?

Dyk ned i procedurelle makroer, herunder brugerdefinerede `#[derive]`-makroer og funktionslignende makroer, til mere kompleks kodegenerering. Du øver dig i Lær at programmere i Rust med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.

Skal jeg have erfaring for at begynde på Lær at programmere i Rust?

Der kræves ingen tidligere erfaring. Lær at programmere i Rust på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 2 af 3.

Hvor lang tid tager lektionen “Procedurelle makroer: derive og funktioner”?

De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.

Kan jeg skrive og køre kode i denne Lær at programmere i Rust-lektion?

Ja. Alle Lær at programmere i Rust-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.

Alle lektioner i dette kursus

  1. Deklarative makroer (`macro_rules!`)
  2. Procedurelle makroer: derive og funktioner
  3. Arbejde med unsafe Rust
← Tilbage til Lær at programmere i Rust