2017-06-03 4 views
0

Ich versuche, ein Merkmal für Top-Level-Funktionen in Rust nutzbar machen.Fehlende Implementierung für Top-Level-Funktionen

trait FnTrait { 
    fn call(self); 
} 

impl FnTrait for fn() { 
    fn call(self) { 
     self() 
    } 
} 

fn foo() { 
    println!("Hello, World!") 
} 

fn main() { 
    FnTrait::call(foo) 
} 

jedoch der Code unten mit (Playground Link)

error[E0277]: the trait bound `fn() {foo}: FnTrait` is not satisfied 
    --> <anon>:16:5 
    | 
16 |  FnTrait::call(foo) 
    |  ^^^^^^^^^^^^^ the trait `FnTrait` is not implemented for `fn() {foo}` 
    | 
    = help: the following implementations were found: 
      <fn() as FnTrait> 
    = note: required by `FnTrait::call` 

ich, dass ich es foo durch Gießen zu kompilieren Trick kann wie so

FnTrait::call(foo as fn()) 

gefunden zu kompilieren schlägt fehl Aber es ist ärgerlich und Einige der Funktionen in meinem Programm sind komplizierter als foo. Irgendeine Möglichkeit, die Besetzung zu vermeiden? Ist mein Zug irgendwie falsch?

Antwort

3

Jede Funktion in Rust hat einen eigenen Typ. Wie Sie sehen können, foo ist kein fn(), es ist ein fn() {foo}; Leider ist dies kein tatsächlicher Typ, den Sie in Source schreiben können, das ist nur eine Compiler-Nachricht. Der Unterschied besteht darin, dass der Compiler es Ihnen leichter macht, Funktionen als Werte zu übergeben und gleichzeitig die Aufrufe zu verknüpfen.

Die Konsequenz ist, dass benannte Funktionszeiger nicht in allgemeine Funktionszeiger umgewandelt werden können, ohne dass ein Cast oder ein Typhinweis angezeigt wird. Zum Beispiel funktioniert das:

Allerdings weiß ich keine Möglichkeit, dies zu nutzen, um das Merkmal zu arbeiten.

Der einzige Weg ist nach vorn zu stoppen versuchen, das Merkmal für Funktionszeiger, zu implementieren und stattdessen es für alles implementieren aufrufbar: (. Halb relevante Antwort über the difference between Fn, FnMut and FnOnce)

trait FnTrait { 
    fn call(self); 
} 

impl<F> FnTrait for F where F: FnOnce() { 
    fn call(self) { 
     self() 
    } 
} 

fn foo() { 
    println!("Hello, World!") 
} 

fn main() { 
    foo.call(); 
} 

Dies funktioniert für alles, was mit dieser Signatur aufgerufen werden kann, einschließlich Funktionen und Schließungen. Der Nachteil ist, dass Sie nur eine solche Implementierung haben können. Sie können dieses Merkmal für keine andere Signatur implementieren.

Eine generische Implementierung oder viele spezifische Implementierungen und viel manuelles Casting. Wähle dein Gift.


Als beiseite: es gibt nicht so etwas wie eine „Top-Level-Funktion“ in Rust, zumindest nicht als Ding unterscheidet sich von anderen Arten von Funktionen. Funktionen sind Funktionen, egal wo sie erscheinen. Instanzfunktionen a.k.a. Methoden sind immer noch normale Funktionen, nur ihr erstes Argument heißt "self".

Verwandte Themen