La macro dbg! e .inspect()

dbg!

dbg! es una macro muy útil para volcar información. Es una buena alternativa a println! porque es más fácil de teclear y da más información.

fn main() {
    let my_number = 8;
    dbg!(my_number);
}

Esto imprime [src\main.rSs:4] my_number = 8.

Además, dbg! se puede poner en muchos otros lugares, incluso envolviendo código en ella. Por ejemplo, en el siguiente código:

fn main() {
    let mut my_number = 9;
    my_number += 10;

    let new_vec = vec![8, 9, 10];

    let double_vec = new_vec.iter().map(|x| x * 2).collect::<Vec<i32>>();
}

Se crea una variable mutable y se modifica después. A continuación se crea un vec y se utiliza iter, map y collect para crear un nuevo vec. Se puede añadir dbg! en casi cualquier sitio del código anterior. Lo que hace esta macro es pedirle al compilador que "le diga qué está haciendo en este momento" y este se lo dice.

fn main() {
    let mut my_number = dbg!(9);
    dbg!(my_number += 10);

    let new_vec = dbg!(vec![8, 9, 10]);

    let double_vec = dbg!(new_vec.iter().map(|x| x * 2).collect::<Vec<i32>>());

    dbg!(double_vec);
}

El código anterior imprime primero:

[src\main.rs:3] 9 = 9

y después:

[src\main.rs:4] my_number += 10 = ()

y sigue con:

[src\main.rs:6] vec![8, 9, 10] = [
    8,
    9,
    10,
]

y la siguiente muestra incluso el valor de la expresión:

[src\main.rs:8] new_vec.iter().map(|x| x * 2).collect::<Vec<i32>>() = [
    16,
    18,
    20,
]

y:

[src\main.rs:10] double_vec = [
    16,
    18,
    20,
]

.inspect()

.inspect() es parecido a dbg!, pero se usa como .map en un iterador. Recibe un elemento iterador y se puede hacer lo que se quiera con él, el valor continuará en la cadena de funciones sin modificar. Por ejemplo, en el siguiente código:

fn main() {
    let new_vec = vec![8, 9, 10];

    let double_vec = new_vec
        .iter()
        .map(|x| x * 2)
        .collect::<Vec<i32>>();
}

Se quiere tener información sobre lo que va haciendo el código. Así que se puede añadir .inspect() en dos puntos:

fn main() {
    let new_vec = vec![8, 9, 10];

    let double_vec = new_vec
        .iter()
        .inspect(|first_item| println!("El elemento contiene: {}", first_item))
        .map(|x| x * 2)
        .inspect(|next_item| println!("Y después vale: {}", next_item))
        .collect::<Vec<i32>>();
}

Que imprime:

El elemento contiene: 8
Y después vale: 16
El elemento contiene: 9
Y después vale: 18
El elemento contiene: 10
Y después vale: 20

Como .inspect() recibe de parámetro un cierre (closure), se puede codificar todo lo que se necesite:

fn main() {
    let new_vec = vec![8, 9, 10];

    let double_vec = new_vec
        .iter()
        .inspect(|first_item| {
            println!("El elemento es: {}", first_item);
            match **first_item % 2 { // eL primer elemento es un &i32 **
                0 => println!("Es par."),
                _ => println!("Es impar."),
            }
            println!("En binario es {:b}.", first_item);
        })
        .map(|x| x * 2)
        .collect::<Vec<i32>>();
}

Que imprime:

El elemento es: 8
Es par.
En binario es 1000.
El elemento es: 9
Es impar.
En binario es 1001.
El elemento es: 10
Es par.
En binario es 1010.