2015-10-01 6 views
11

Ich versuche, eine Funktion zu wählen, die abhängig von einer Bedingung aufgerufen wird. Ich möchte diese Funktion in einer Variablen speichern, damit ich sie später wieder aufrufen kann, ohne die Bedingung zu tragen. Hier ist ein funktionierendes minimales Beispiel:Wählen Sie dynamisch eine Funktion zum Aufrufen ohne Zwischenvariablen

fn foo() { 
    println! ("Foo"); 
} 
fn bar() { 
    println! ("Bar"); 
} 

fn main() { 
    let selector = 0; 

    let foo: &Fn() = &foo; 
    let bar: &Fn() = &bar; 
    let test = match selector { 
     0 => foo, 
     _ => bar 
    }; 
    test(); 
} 

Meine Frage ist: Ist es möglich, die Zwischenvariablen loszuwerden? Ich habe einfach versucht, zu entfernen sie:

fn foo() { 
    println! ("Foo"); 
} 
fn bar() { 
    println! ("Bar"); 
} 

fn main() { 
    let selector = 0; 

    let test = match selector { 
     0 => &foo as &Fn(), 
     _ => &bar as &Fn() 
    }; 
    test(); 
} 

aber dann der borrow checker beklagt, dass die geliehenen Werte bis zum Ende des Spiels nur gültig sind (btw, warum die Funktionen sind 'static ohnehin so sollte die Gültigkeit? Ende der Zeiten). Ich habe auch versucht, die 'static Lebensdauer explizit zu machen, indem ich &foo as &'static Fn() verwende, aber das funktioniert auch nicht.

Antwort

5

Die folgenden Werke, wenn Sie nur mit statischen Funktionen und nicht die Verschlüsse arbeiten müssen: statt Funktion Merkmal

fn foo() { 
    println!("Foo"); 
} 

fn bar() { 
    println!("Bar"); 
} 

fn main() { 
    let selector = 0; 

    let test: fn() = match selector { 
     0 => foo, 
     _ => bar 
    }; 

    test(); 
} 

(anprobieren playground)

Hier habe ich Funktionstyp verwendet.

Der Grund, dass das ausgeliehene Merkmalsobjekt nicht funktioniert, ist wahrscheinlich das folgende. Jedes Merkmalsobjekt ist ein Fettzeiger, der aus einem Zeiger auf einen Wert und einem Zeiger auf eine virtuelle Tabelle besteht. Wenn das Merkmalsobjekt aus einem Abschluss erzeugt wird, ist alles klar - der Wert würde durch den Abschluss selbst repräsentiert (intern eine Instanz einer Struktur, die alle erfassten Variablen enthält) und die virtuelle Tabelle würde einen Zeiger auf die Implementierung des enthalten Entsprechendes Fn*() Merkmal, das vom Compiler generiert wird, dessen Körper der Schließkörper wäre.

Mit Funktionen sind die Dinge jedoch nicht so klar. Es gibt keinen Wert zum Erstellen eines Merkmalsobjekts, da die Funktion selbst der Implementierung des Merkmals Fn() entsprechen sollte. Daher erzeugt rustc wahrscheinlich eine leere Struktur und implementiert Fn() für sich und diese Implementierung ruft die statische Funktion direkt (nicht unbedingt Rust, aber etwas in der Nähe):

struct SomeGeneratedStructFoo; 

impl Fn<()> for SomeGeneratedStructFoo { 
    type Output =(); 
    fn call(&self, args:()) ->() { 
     foo(); 
    } 
} 

Wenn daher ein Merkmal Objekt aus fn foo() erstellt wird ein Verweis auf einen temporären Wert vom Typ SomeGeneratedStructFoo genommen. Dieser Wert wird jedoch innerhalb der Übereinstimmung erstellt, und nur ein Verweis darauf wird von der Übereinstimmung zurückgegeben. Daher wird dieser Wert nicht lange genug verwendet, und darum geht es im Fehler.

1

fn() ist ein Funktionszeigertyp. Es ist bereits ein Zeigertyp. Sie können dies mit std::mem::size_of::<fn()>() überprüfen. Es ist kein Null-Typ.

Wenn Sie &foo tun, nehmen Sie einen Zeiger auf einen Stack zugeordneten Zeiger. Dieser innere Zeiger überlebt nicht sehr lange und verursacht den Fehler.

Sie können diese in den allgemeinen Typ fn() wie vorgeschlagen umwandeln. Ich wäre daran interessiert zu wissen, warum Sie nicht fn() zu &Fn(), obwohl.