13. Macros
Les macros en Rust sont du code qui génère du code. Il existe deux types : les macros déclaratives (
macro_rules!) pour le pattern matching sur le code source, et les macros procédurales (#[derive],#[tokio::main]) pour des transformations plus puissantes.
13.1 Macro Rules (macro_rules!)
Déclaration
macro_rules! nom_de_la_macro {
($arg:expr) => {
// code généré
};
}Exemples utiles
// Macro simple : log avec niveau
macro_rules! log_gradient {
($level:expr, $msg:expr) => {
println!("[{}] {}", $level, $msg);
};
}
log_gradient!("INFO", "gradient reçu"); // [INFO] gradient reçu
// Macro avec plusieurs arguments
macro_rules! create_gar {
($name:ident, $point_rupture:expr) => {
struct $name;
impl $name {
fn breakdown_point(&self) -> f64 { $point_rupture }
}
};
}
create_gar!(Median, 0.5);
create_gar!(Krum, 0.5);
let m = Median;
println!("{}", m.breakdown_point()); // 0.5Macro avec répétition
macro_rules! gradient_matrix {
( $( $row:expr ),+ ) => {
vec![
$( vec![$row] ),+
]
};
}
let grads = gradient_matrix!(
[1.0, 2.0, 3.0],
[4.0, 5.0, 6.0],
[7.0, 8.0, 9.0]
);
// = vec![vec![1.0, 2.0, 3.0], vec![4.0, 5.0, 6.0], vec![7.0, 8.0, 9.0]]Pattern matching dans les macros
macro_rules! compute_median {
// Cas : liste de valeurs
($($val:expr),+) => {
{
let mut v = vec![$($val),+];
v.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap());
v[v.len() / 2]
}
};
// Cas : vecteur
($vec:expr) => {
{
let mut v = $vec;
v.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap());
v[v.len() / 2]
}
};
}
let m1 = compute_median!(3, 1, 4, 1, 5); // 3
let v = vec![9.0, 1.0, 5.0];
let m2 = compute_median!(v); // 5.013.2 Macros de la bibliothèque standard
Les plus utilisées :
// vec! — la plus familière
let v = vec![1, 2, 3];
// println! / format!
println!("valeur: {}", x);
let s = format!("{:.2}", pi);
// assert!
assert_eq!(x, 42);
assert!(x > 0, "x doit être positif");
// todo! / unimplemented!
fn future_feature() {
todo!("implémenter plus tard");
}
// matches!
if matches!(status, Status::Active | Status::Pending) {
println!("en cours");
}
// cfg! (compilation conditionnelle)
if cfg!(target_os = "linux") {
println!("spécifique Linux");
}
// include_str! / include_bytes!
const CONFIG: &str = include_str!("config.toml");
const MODEL_HEADER: &[u8] = include_bytes!("model_header.bin");13.3 Macros Procédurales
Les macros procédurales sont du code Rust qui s’exécute à la compilation pour transformer le code source.
Derive Macro (#[derive(Trait)])
use serde::{Serialize, Deserialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
struct GradientConfig {
dimension: usize,
n_workers: usize,
}
// Sérialisation et désérialisation automatiquesCréer sa propre derive macro (avancé)
// Dans un crate séparé : gradient-derive/Cargo.toml
// [lib]
// proc-macro = true
// gradient-derive/src/lib.rs
use proc_macro::TokenStream;
#[proc_macro_derive(GarInfo)]
pub fn gar_info_derive(input: TokenStream) -> TokenStream {
// Parser le struct, ajouter une méthode broken_point()
let input = parse_macro_input!(input as DeriveInput);
let name = &input.ident;
let expanded = quote! {
impl #name {
pub fn breakdown_point(&self) -> f64 {
0.5 // exemple
}
}
};
expanded.into()
}13.4 Exemples Concrets
Macro pour construire une config
macro_rules! config {
($($key:ident: $value:expr),* $(,)?) => {
{
let mut c = ExperimentConfig::default();
$( c.$key = $value; )*
c
}
};
}
let cfg = config! {
learning_rate: 0.001,
gar: "median".to_string(),
n_workers: 50,
f: 10,
};Macro pour mesurer le temps
macro_rules! time {
($name:expr, $code:expr) => {
{
let start = std::time::Instant::now();
let result = $code;
let elapsed = start.elapsed();
println!("[TIMING] {}: {:?}", $name, elapsed);
result
}
};
}
let result = time!("krum 50×10k", {
let krum = Krum::new(10);
krum.aggregate(&large_grads).unwrap()
});13.5 Résumé
| Type de macro | Syntaxe | Usage |
|---|---|---|
| Déclarative | macro_rules! | Code répétitif, DSL simples |
| Derive | #[derive(Trait)] | Implémentation automatique de traits |
| Attribute | #[tokio::main] | Transformation de fonctions/modules |
| Function-like | my_macro!() | Comme une fonction mais sur le code source |
Règle : quand un pattern se répète, faites-en une macro. Mais préférez une fonction quand c’est possible.