2017-01-14 2 views
2

Ich versuche, ein Makro zu erstellen, die ein struct erzeugt, die eine Reihe von Methoden bereitstellt, die in das Makro übergeben werden. Zum Beispiel, Aufruf:Ist es möglich, einen Elementarg zu verwenden, der als Methode an ein Makro übergeben wird?

create_impl!(StructName, fn foo() -> u32 { return 432 }) 

sollte eine leere Struktur StructName erzeugen, die die Methode foo() zur Verfügung stellt.

Mein erster Versuch dazu verwendet die item Makro arg Typ. Allerdings, wenn ich versuche und ein item in der Regel verwenden, erhalte ich den folgende Compiler-Fehler:

error: expected one of `const`, `default`, `extern`, `fn`, `pub`, `type`, `unsafe`, or `}`, found `fn foo() -> u32 { return 42; }` 
    --> src/lib.rs:40:13 
    | 
40 |   $($function)* 
    |    ^^^^^^^^^ 

Ist es möglich, item Argumente zu verwenden Methoden erzeugten Strukturen auf diese Weise zu definieren? Gibt es etwas, das mir fehlt?

Hier ist das vollständige Makro I definiert haben:

macro_rules! create_impl { 

    ($struct_name:ident, $($function:item),*) => { 
     struct $struct_name { 
     } 

     impl $struct_name { 
      // This is the part that fails. 
      $($function)* 
     } 
    }; 

} 
+1

Ich denke, dass * Methoden * überhaupt keine Elemente sind.Wenn ich 'fn foo()' in 'fn foo (self)' ändere, bekomme ich * error: erwartet eines von '::' oder ':', found ')' * (der gleiche Fehler tritt auf, wenn du das draussen schreibst ein Makro). –

Antwort

1

Die kurze Antwort ist: „Nein, Sie können nicht die item Matcher für ein Verfahren verwenden“.

Nach der reference, Elemente sind die Top-Level-Dinge in einer Kiste oder Modul, also Funktionen, Typen und so weiter. Während ein struct oder impl Block ein Element ist, sind die Dinge in ihnen nicht. Obwohl eine Methodendefinition syntaktisch mit einer Funktion auf oberster Ebene identisch ist, wird sie dadurch nicht zu einem Element.

so funktioniert, Rust Makro-System ist, dass, sobald ein Fragment als item analysiert wurde, z.B. mit $foo:item ist es dann für immer ein item; Wenn das Makro erweitert wird, wird es in Tokens aufgeteilt, um es erneut zu reparieren.

Das Ergebnis davon ist, dass $foo:item kann nur in der Makro-Ausgabe in Artikelposition, die in der Regel Top-Level bedeutet.

Es gibt ein paar Alternativen.

Die einfachste ist die guten alten tt (Token Baum) Matcher zu verwenden. Ein Token-Baum ist entweder ein Nicht-Klammer-Token oder eine Folge von Token, die von symmetrischen Klammern umgeben sind. so passt $(foo:tt)* alles. Das bedeutet jedoch, dass es auch Kommas verschlingt, so dass es einfacher ist, Klammern um jeden Artikel hinzuzufügen:

macro_rules! create_impl {

($struct_name:ident, $({ $($function:tt)* }),*) => { 
     struct $struct_name { 
     } 

     impl $struct_name { 
      $($($function)*)* 
     } 
    }; 

} 

Dann haben Sie es mit den zusätzlichen Klammern zu verwenden:

create_impl!(StructName, { fn foo() -> u32 { return 432 } }, { fn bar() -> u32 { return 765 } }); 

Sie können auch nur die Syntax entsprechen, die Sie direkt möchten, anstatt auf die item Matcher Delegieren:

macro_rules! create_impl2 { 
    ($struct_name:ident, $(fn $fname:ident($($arg:tt)*) -> $t:ty $body:block),*) => { 
     struct $struct_name { 
     } 

     impl $struct_name { 
      $(fn $fname($($arg)*) -> $t $body)* 
     } 
    } 
} 

Da dies explizit ist, bedeutet das natürlich, dass Sie, wenn Sie Funktionen ohne Rückgabetyp unterstützen möchten, Ihrem Makro einen weiteren Fall hinzufügen müssen.

Verwandte Themen