Rust – il linguaggio – 41

Cdg

Sempre sul debug, da qui oggi qui: /usr/local/share/doc/rust/html/book/macros.html#the-variable-crate.

La variabile $crate

A further difficulty occurs when a macro is used in multiple crates. Say that mylib defines

pub fn increment(x: u32) -> u32 {
    x + 1
}

#[macro_export]
macro_rules! inc_a {
    ($x:expr) => ( ::increment($x) )
}

#[macro_export]
macro_rules! inc_b {
    ($x:expr) => ( ::mylib::increment($x) )
}

inc_a only works within mylib, while inc_b only works outside the library. Furthermore, inc_b will break if the user imports mylib under another name.

Rust does not (yet) have a hygiene system for crate references, but it does provide a simple workaround for this problem. Within a macro imported from a crate named foo, the special macro variable $crate will expand to ::foo. By contrast, when a macro is defined and then used in the same crate, $crate will expand to nothing. This means we can write

#[macro_export]
macro_rules! inc {
    ($x:expr) => ( $crate::increment($x) )
}

to define a single macro that works both inside and outside our library. The function name will expand to either ::increment or ::mylib::increment.

To keep this system simple and correct, #[macro_use] extern crate ... may only appear at the root of your crate, not inside mod. This ensures that $crate is a single identifier.

The deep end

The introductory chapter mentioned recursive macros, but it did not give the full story. Recursive macros are useful for another reason: Each recursive invocation gives you another opportunity to pattern-match the macro’s arguments.

As an extreme example, it is possible, though hardly advisable, to implement the Bitwise Cyclic Tag automaton within Rust’s macro system.

macro_rules! bct {

    // cmd 0:  d ... => ...
    (0, $($ps:tt),* ; $_d:tt)
        => (bct!($($ps),*, 0 ; ));
    (0, $($ps:tt),* ; $_d:tt, $($ds:tt),*)
        => (bct!($($ps),*, 0 ; $($ds),*));

    // cmd 1p:  1 ... => 1 ... p
    (1, $p:tt, $($ps:tt),* ; 1)
        => (bct!($($ps),*, 1, $p ; 1, $p));
    (1, $p:tt, $($ps:tt),* ; 1, $($ds:tt),*)
        => (bct!($($ps),*, 1, $p ; 1, $($ds),*, $p));

    // cmd 1p:  0 ... => 0 ...
    (1, $p:tt, $($ps:tt),* ; $($ds:tt),*)
        => (bct!($($ps),*, 1, $p ; $($ds),*));

    // halt on empty data string
    ( $($ps:tt),* ; )
        => (());
}

Mettiamola così: capito poco 😦
A essere interamente onesto: capito niente, nada, zilch 😦
Da approfondire. Uh! ne parla Stack Overflow.
Non è per niente semplice; copio la seconda risposta.

fn main() {
    // "Bitwise Cyclic Tag" automation through macros
    macro_rules! bct {
        // cmd 0:  0 ... => ...
        (0, $($program:tt),* ; $_head:tt)
            => (pbct!($($program),*, 0 ; ));
        (0, $($program:tt),* ; $_head:tt, $($tail:tt),*)
            => (pbct!($($program),*, 0 ; $($tail),*));

        // cmd 1x:  1 ... => 1 ... x
        (1, $x:tt, $($program:tt),* ; 1)
            => (pbct!($($program),*, 1, $x ; 1, $x));
        (1, $x:tt, $($program:tt),* ; 1, $($tail:tt),*)
            => (pbct!($($program),*, 1, $x ; 1, $($tail),*, $x));

        // cmd 1x:  0 ... => 0 ...
        (1, $x:tt, $($program:tt),* ; $($tail:tt),*)
            => (pbct!($($program),*, 1, $x ; $($tail),*));

        // halt on empty data string
        ( $($program:tt),* ; )
            => (());
    }

    macro_rules! println_bct {
        () =>
            (println!(""));
        (;) =>
            (println!(":"));

        ($d:tt) =>
            (println!("{}", stringify!($d)));
        ($d:tt, $($data:tt),*) => {
            print!("{}", stringify!($d));
            println_bct!($($data),*);
        };
        ( ; $($data:tt),*) => {
            print!(":");
            println_bct!($($data),*);
        };

        ($x:tt ; $($data:tt),*) => {
            print!("{}", stringify!($x));
            println_bct!( ; $($data),*);
        };
        ($x:tt, $($program:tt),* ; $($data:tt),*) => {
            print!("{}", stringify!($x));
            println_bct!($($program),* ; $($data),*);
        };
    }

    macro_rules! pbct {
        ($($program:tt),* ; $($data:tt),*) => {
            println_bct!($($program),* ; $($data),*);
            bct!($($program),* ; $($data),*);
        };
    }

    pbct!(0, 0, 1, 1, 1, 0, 0, 0 ; 1, 0, 1);

    // This one causes the compiler to hit recursion limits, heh
    // pbct!(0, 0, 1, 1, 1, 1, 1, 0 ; 1, 0, 1);
}

rs41-13

Le macros più comuni

Here are some common macros you’ll see in Rust code.

panic!
This macro causes the current thread to panic. You can give it a message to panic with:

fn main() {
    panic!("oh no!");
}

rs41-14
vec!
The vec! macro is used throughout the book, so you’ve probably seen it already. It creates Vec<T>s with ease:

fn main() {
    let v = vec![1, 2, 3, 4, 5];
    println!("{}", v[1]);
}

rs41-15

It also lets you make vectors with repeating values. For example, a hundred zeroes:

fn main() {
    let v = vec![0; 100];
    println!("{}", v[42]);    
}

rs41-16
assert! e assert_eq!
These two macros are used in tests. assert! takes a boolean. assert_eq! takes two values and checks them for equality. true passes, false panic!s. Like this:

fn main() {
    // A-ok!
    
    assert!(true);
    assert_eq!(5, 3 + 2);
    
    // nope :(
    
    assert!(5 < 3);
    assert_eq!(5, 3);
}

rs41-17

try!
try! is used for error handling. It takes something that can return a Result<T, E>, and gives T if it’s a Ok<T>, and returns with the Err(E) if it’s that. Like this:

use std::fs::File;

fn foo() -> std::io::Result {
    let f = try!(File::create("foo.txt"));

    Ok(())
}

This is cleaner than doing this:

use std::fs::File;

fn foo() -> std::io::Result {
    let f = File::create("foo.txt");

    let f = match f {
        Ok(t) => t,
        Err(e) => return Err(e),
    };

    Ok(())
}

unreachable!
This macro is used when you think some code should never execute:

fn main() {
    if false {
        unreachable!();
    }
    println!("OK!");
}

rs41-18

Sometimes, the compiler may make you have a different branch that you know will never, ever run. In these cases, use this macro, so that if you end up wrong, you’ll get a panic! about it.

fn main() {
    let x: Option = None;
    
    match x {
        Some(_) => unreachable!(),
        None => println!("I know x is None!"),
    }
}

rs41-19
unimplemented!
The unimplemented! macro can be used when you’re trying to get your functions to typecheck, and don’t want to worry about writing out the body of the function. One example of this situation is implementing a trait with multiple required methods, where you want to tackle one at a time. Define the others as unimplemented! until you’re ready to write them.

Procedural macros

If Rust’s macro system can’t do what you need, you may want to write a compiler plugin [/usr/local/share/doc/rust/html/book/compiler-plugins.html] instead. Compared to macro_rules! macros, this is significantly more work, the interfaces are much less stable, and bugs can be much harder to track down. In exchange you get the flexibility of running arbitrary Rust code within the compiler. Syntax extension plugins are sometimes called ‘procedural macros’ for this reason.

😀 OK, le macros sono da sempre ardue, il debug pure, ma ora si cambia argomento 😀
:mrgreen:

Posta un commento o usa questo indirizzo per il trackback.

Trackback

Rispondi

Inserisci i tuoi dati qui sotto o clicca su un'icona per effettuare l'accesso:

Logo di WordPress.com

Stai commentando usando il tuo account WordPress.com. Chiudi sessione /  Modifica )

Google photo

Stai commentando usando il tuo account Google. Chiudi sessione /  Modifica )

Foto Twitter

Stai commentando usando il tuo account Twitter. Chiudi sessione /  Modifica )

Foto di Facebook

Stai commentando usando il tuo account Facebook. Chiudi sessione /  Modifica )

Connessione a %s...

Questo sito utilizza Akismet per ridurre lo spam. Scopri come vengono elaborati i dati derivati dai commenti.

%d blogger hanno fatto clic su Mi Piace per questo: