Macro procedurali: Derive e Function
Approfondisca le macro procedurali, incluse le macro `#[derive]` personalizzate e le macro simili a funzioni, per generare codice più complesso.
Macro procedurali: Derive e Function è una lezione Learn Rust Coding gratuita su CoddyKit. Questa è la lezione 2 di 3. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Learn Rust Coding, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Learn Rust Coding include 3 lezioni in totale.
Introduzione alle macro procedurali
Benvenuto nel mondo delle macro procedurali! A differenza delle macro dichiarative (macro_rules!), le macro procedurali sono simili a funzioni che operano direttamente sul codice Rust.
Ricevono in input un TokenStream, lo elaborano e restituiscono un altro TokenStream. Questo consente di generare codice molto più complesso.
Tipi di macro procedurali
Rust offre tre tipi principali di macro procedurali:
- Macro simili a funzioni: Hanno l'aspetto e il comportamento delle normali chiamate di funzione, ad esempio
my_macro!(...). - Macro derive: Consentono di implementare automaticamente i trait per struct ed enum usando
#[derive(MyTrait)]. - Macro di attributo: Consentono di definire attributi personalizzati da applicare agli elementi, ad esempio
#[route("/path")].
Il crate `proc-macro`
Le macro procedurali devono risiedere in un tipo speciale di crate. Nel file Cargo.toml, lo dichiara in questo modo:
In questo modo comunica a Cargo che il crate contiene macro che trasformano il codice in fase di compilazione.
[package]
name = "my_macros"
version = "0.1.0"
edition = "2021"
[lib]
proc-macro = trueSpiegazione delle macro simili a funzioni
Le macro simili a funzioni vengono definite applicando l'attributo #[proc_macro] a una funzione che riceve un proc_macro::TokenStream e ne restituisce uno.
Sono utili per DSL personalizzati (linguaggi specifici di dominio) o per ripetizioni di codice complesse che macro_rules! non è in grado di gestire.
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
}Esempio di macro simile a una funzione
Ecco un esempio concettuale: una macro `greet_name!` che genera un'istruzione di stampa. L'implementazione effettiva analizzerebbe l'input TokenStream per estrarre il nome.
Sebbene la definizione della macro sia complessa, il suo utilizzo è semplice e potente:
/* 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");
}Capire le macro `#[derive]`
Le macro derive sono il tipo più comune. Consentono di implementare automaticamente un trait per una struct o un enum.
Quando scrive #[derive(Debug)], una macro viene eseguita in fase di compilazione per generare il codice necessario affinché il tipo implementi il trait Debug.
Implementare un `#[derive]` (concetto)
Le macro derive personalizzate vengono generalmente realizzate usando librerie come syn (per analizzare il codice Rust trasformandolo in un AST) e quote (per generare codice Rust a partire da un AST).
Si definisce una funzione annotata con #[proc_macro_derive(MyTrait)] che riceve un TokenStream rappresentante la struct o l'enum e restituisce l'implementazione generata del trait.
/* 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()
}Esempio con `#[derive(Debug)]`
Vediamo un esempio pratico di una macro derive che probabilmente ha già usato: Debug. Aggiungendo #[derive(Debug)] alla nostra struct Point, Rust implementa automaticamente il trait Debug, consentendoci di stampare i suoi contenuti in modo leggibile.
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let p = Point { x: 10, y: 20 };
println!("My point is: {:?}", p);
}Macro di attributo: `#[my_attribute]`
Le macro di attributo sono simili alle macro derive, ma possono essere applicate a qualsiasi elemento (funzioni, struct, moduli e così via). Ricevono in input l'elemento a cui sono associate, insieme agli eventuali argomenti tra parentesi.
Tra gli usi comuni rientrano il routing nei framework web (ad esempio #[get("/users")]) e la configurazione dei test (ad esempio #[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();
}Quando usare ciascun tipo di macro
- Macro simili a funzioni: Per mini-linguaggi personalizzati o trasformazioni complesse di blocchi di codice arbitrari.
- Macro derive: Per implementare automaticamente i trait su struct ed enum.
- Macro di attributo: Per modificare o generare codice attorno a elementi specifici (funzioni, struct) in base ad attributi personalizzati.
Ogni tipo svolge una funzione specifica nell'estensione della sintassi e delle funzionalità di Rust.
Verifica rapida: tipi di macro
Quali delle seguenti affermazioni sulle macro procedurali sono VERE?
Riepilogo: la potenza della generazione del codice
Ha esplorato il mondo avanzato delle macro procedurali!
- Ha imparato a conoscere le macro simili a funzioni per la sintassi personalizzata.
- Ha capito come le macro
#[derive]automatizzano l'implementazione dei trait. - Ha esaminato brevemente le macro di attributo per la generazione di codice a livello di elemento.
Le macro procedurali sono uno strumento potente per ridurre il codice ripetitivo ed estendere le funzionalità di Rust, consentendo di scrivere codice che scrive codice!
Domande Frequenti
La lezione «Macro procedurali: Derive e Function» è gratuita?
Sì — il testo completo di «Macro procedurali: Derive e Function» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Learn Rust Coding, passa a CoddyKit PRO. Il corso Learn Rust Coding include 3 lezioni in totale.
Cosa imparerò in «Macro procedurali: Derive e Function»?
Approfondisca le macro procedurali, incluse le macro `#[derive]` personalizzate e le macro simili a funzioni, per generare codice più complesso. Eserciti Learn Rust Coding con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.
Ho bisogno di esperienza per iniziare Learn Rust Coding?
Non è richiesta alcuna esperienza precedente. Learn Rust Coding su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 2 di 3.
Quanto tempo richiede la lezione «Macro procedurali: Derive e Function»?
La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.
Posso scrivere ed eseguire codice in questa lezione Learn Rust Coding?
Sì. Ogni lezione Learn Rust Coding include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.
Tutte le lezioni di questo corso
- Macro dichiarative (`macro_rules!`)
- Macro procedurali: Derive e Function
- Interagire con Rust unsafe