¿Es posible declarar variables de forma procesal utilizando macros de Rust?

StackOverflow https://stackoverflow.com//questions/23061702

  •  26-12-2019
  •  | 
  •  

Pregunta

Básicamente, esta pregunta tiene dos partes:

  1. ¿Puedes pasar un identificador desconocido a una macro en Óxido?

  2. ¿Puedes combinar cadenas para generar nuevos nombres de variables en una macro de Rust?

Por ejemplo, algo como:

macro_rules! expand(
  ($x:ident) => (
    let mut x_$x = 0;
  )
)

Llamar a expandir!(hola) obvio falla porque hola es un identificador desconocido;pero ¿puedes hacer esto de alguna manera?

es decir.El equivalente en C de algo como:

#include <stdio.h>
#define FN(Name, base) \
  int x1_##Name = 0 + base; \
  int x2_##Name = 2 + base; \
  int x3_##Name = 4 + base; \
  int x4_##Name = 8 + base; \
  int x5_##Name = 16 + base;

int main() {
  FN(hello, 10)
  printf("%d %d %d %d %d\n", x1_hello, x2_hello, x3_hello, x4_hello, x5_hello);
  return 0;
}

¿Por qué dices que idea tan terrible?¿Por qué querrías hacer eso?

¡Me alegra que hayas preguntado!

Considere este bloque de óxido:

{
   let marker = 0;
   let borrowed = borrow_with_block_lifetime(data, &marker); 
   unsafe {
      perform_ffi_call(borrowed);
   }
}

Ahora tiene un valor prestado con una duración (marcador) explícitamente limitada que no utiliza una duración de estructura, pero que podemos garantizar que existe para todo el alcance de la llamada ffi;al mismo tiempo no nos topamos con errores oscuros en los que un * se elimina la referencia de manera insegura dentro de un bloque inseguro y, por lo tanto, el compilador no lo detecta como un error, a pesar de que se cometió el error. dentro de un bloque seguro.

(ver también ¿Por qué todos mis punteros apuntan al mismo lugar con to_c_str() en óxido?)

El uso de una macro que pueda declarar variables temporales para este propósito aliviaría considerablemente los problemas que tengo con el compilador.Por eso quiero hacer esto.

¿Fue útil?

Solución

Sí, puede pasar un identificador arbitrario a una macro y sí, puede concatenar identificadores en un nuevo identificador usando concat_idents!() macro:

#![feature(concat_idents)]

macro_rules! test {
    ($x:ident) => ({
        let z = concat_idents!(hello_, $x);
        z();
    })
}

fn hello_world() {  }

fn main() {
    test!(world);
}

Sin embargo, hasta donde yo sé, porque concat_idents!() en sí es una macro, no puedes usar este identificador concatenado en todos lados podrías usar un identificador simple, solo en ciertos lugares, como en el ejemplo anterior, y esto, en mi opinión, es un ENORME inconveniente.Ayer mismo intenté escribir una macro que pudiera eliminar muchos textos repetitivos de mi código, pero al final no pude hacerlo porque las macros no admiten la colocación arbitraria de identificadores concatenados.

Por cierto, si entiendo su idea correctamente, realmente no necesita concatenar identificadores para obtener nombres únicos.Las macros de Rust, a diferencia de las C, son higiénico.Esto significa que todos los nombres de las variables locales introducidas dentro de una macro no se filtrarán al ámbito donde se llama esta macro.Por ejemplo, podrías asumir que este código funcionaría:

macro_rules! test {
    ($body:expr) => ({ let x = 10; $body })
}

fn main() {
    let y = test!(x + 10);
    println!("{}", y);
}

Es decir, creamos una variable. x y poner una expresión después de su declaración.Entonces es natural pensar que x en test!(x + 10) se refiere a esa variable declarada por la macro, y todo debería estar bien, pero de hecho este código no se compilará:

main3.rs:8:19: 8:20 error: unresolved name `x`.
main3.rs:8     let y = test!(x + 10);
                             ^
main3.rs:3:1: 5:2 note: in expansion of test!
main3.rs:8:13: 8:27 note: expansion site
error: aborting due to previous error

Entonces, si todo lo que necesita es la unicidad de los locales, entonces no puede hacer nada con seguridad y usar los nombres que desee, serán únicos automáticamente.Esto es explicado en el tutorial de macros, aunque el ejemplo me parece algo confuso.

Otros consejos

En los casos en que concat_idents no funciona (que en la mayoría de los casos me gustaría usarlo) cambiar el problema de identificadores concatenados al uso de espacios de nombres funciona.

Es decir, en lugar del código que no funciona:

macro_rules! test {
    ($x:ident) => ({
        struct concat_idents!(hello_, $x) {}
        enum contact_idents!(hello_, $x) {}
    })
}

El usuario puede nombrar el espacio de nombres y luego tener nombres preestablecidos como se muestra a continuación:

macro_rules! test {
    ($x:ident) => ({
        mod $x {
            struct HelloStruct {}
            enum HelloEnum {}
        }
    })
}

Ahora tienes un nombre basado en el argumento de la macro.Esta técnica sólo es útil en casos específicos.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top