2014-04-14 13 views
6

Grundsätzlich gibt es zwei Teile auf diese Frage:Ist es möglich, Variablen prozedural mit Rust-Makros zu deklarieren?

  1. Können Sie eine unbekannte Kennung zu einem Makro in Rust passieren?

  2. Können Sie Zeichenfolgen kombinieren, um neue Variablennamen in einem Rust-Makro zu generieren? !

Zum Beispiel so etwas wie:

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

Aufruf erweitern (hallo) offensichtlich schlägt fehl, da hallo eine unbekannte Kennung ist; aber kannst du das irgendwie machen?

dh. Das Äquivalent in C von etwas wie:

#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; 
} 

Warum Sie sagen, was für eine schreckliche Idee. Warum würdest du das jemals tun wollen?

Ich bin froh, dass Sie gefragt haben!

diesen Rostblock vor:

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

Sie jetzt einen geliehenen Wert mit einer explizit begrenzten Lebensdauer hat (Marker), die keine Struktur Lebensdauer verwendet, sondern dass wir existiert für den gesamten Umfang garantieren können der Anruf ffi; zur selben Zeit kommen wir nicht zu obskuren Fehlern, wo ein * unsicher in einem unsicheren Block de-referenziert wird und so der Compiler es nicht als ein Fehler abfängt, trotz des Fehlers innerhalb eines sicheren Blocks gemacht wird.

(siehe auch Why are all my pointers pointing to the same place with to_c_str() in rust?)

die Verwendung eines Makros, die temporäre Variablen für diesen Zweck erheblich die Probleme lindern würde erklären kann ich mit dem Compiler haben zu kämpfen. Deshalb möchte ich das machen.

Antwort

9

Ja, können Sie beliebige Bezeichner in ein Makro und ja passieren, können Sie Bezeichner in eine neue Kennung verketten concat_idents!() Makro weiter:

#![feature(concat_idents)] 

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

fn hello_world() { } 

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

aber soweit ich weiß, weil concat_idents!() selbst ein Makro , Sie können diese verkettete Kennung überall nicht verwenden Sie könnten einfache Kennung nur an bestimmten Orten wie im obigen Beispiel verwenden, und dies ist meiner Meinung nach ein RIESIGER Nachteil. Gestern habe ich versucht, ein Makro zu schreiben, das eine Menge Textbausteine ​​in meinem Code entfernen könnte, aber letzten Endes war ich nicht in der Lage, das zu tun, da Makros die zufällige Platzierung von verketteten Bezeichnern nicht unterstützen.

BTW, wenn ich Ihre Idee richtig verstehe, brauchen Sie nicht wirklich Verkettung Identifier, um eindeutige Namen zu erhalten. Rostmakros sind, im Gegensatz zu C, hygienic. Dies bedeutet, dass alle Namen von lokalen Variablen, die innerhalb eines Makros eingeführt werden, nicht in den Bereich gelangen, in dem dieses Makro aufgerufen wird. Zum Beispiel könnte man davon ausgehen, dass dieser Code funktionieren würde:

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

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

Das heißt, erstellen wir eine Variable x und einen Ausdruck nach seiner Erklärung setzen.Es ist dann natürlich zu denken, dass x in test!(x + 10) auf diese Variable durch das Makro erklärt bezieht, und alles sollte in Ordnung sein, aber dieser Code in der Tat nicht kompiliert:

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 

Also, wenn alles, was Sie brauchen, ist Einzigartigkeit Einheimische, dann können Sie sicher nichts tun und verwenden Sie alle gewünschten Namen, sie werden automatisch eindeutig sein. Dies ist explained im Makro Tutorial, obwohl ich das Beispiel dort etwas verwirrend finde.

+1

concat_idents könnte ich entfernt, es war bereits Feature-Gated [# 13295] (https://github.com/mozilla/rust/pull/13295) und [# 13294] (https://github.com/mozilla/rust/issues/13294) – Arjan

+0

Wir brauchen diese Funktion, die mit ## in C vergleichbar ist. Ich muss ständig Makros schreiben, wo Benutzer mir zwei Namen geben, weil String Concat so begrenzt ist. –

1

In Fällen, in denen concat_idents nicht funktioniert (die meisten Fälle möchte ich es verwenden) funktioniert das Ändern des Problems von verketteten Bezeichnern auf die Verwendung von Namespaces funktioniert.

, dass anstelle des nicht funktionierenden Code ist:

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

Der Benutzer kann den Namespace-Namen können, und haben dann voreingestellten Namen wie unten dargestellt:

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

Jetzt haben Sie einen Namen basierend auf dem Argument des Makros. Diese Technik ist nur in bestimmten Fällen hilfreich.

Verwandte Themen