2015-10-01 6 views
11

Sto provando a selezionare una funzione da chiamare in base a una condizione. Voglio memorizzare quella funzione in una variabile in modo da poterla richiamare più tardi senza portare con sé le condizioni. Ecco un esempio minimo di lavoro:Seleziona dinamicamente una funzione da chiamare senza variabili intermedie

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(); 
} 

La mia domanda è: è possibile liberarsi delle variabili intermedie? Ho provato semplicemente rimuovendo:

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(); 
} 

ma poi il correttore prestito lamenta che i valori presi in prestito sono validi solo fino alla fine della partita (a proposito, il motivo per cui le funzioni sono 'static comunque così dovrebbe essere valida per il? fine dei tempi). Ho anche provato a rendere esplicita la durata 'static usando &foo as &'static Fn() ma non funziona neanche.

risposta

5

le seguenti opere, se avete solo bisogno di lavorare con funzioni statiche e non chiusure:

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

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

fn main() { 
    let selector = 0; 

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

    test(); 
} 

(provate sul playground)

Qui ho utilizzato tipo di funzione al posto della funzione caratteristica.

Il motivo per cui l'oggetto tratto in prestito non funziona è probabilmente il seguente. Qualsiasi oggetto tratto è un puntatore grasso che consiste in un puntatore a un valore e un puntatore a una tabella virtuale. Quando l'oggetto tratto è creato da una chiusura, tutto è chiaro - il valore sarebbe rappresentato dalla chiusura stessa (essendo internamente un'istanza di una struttura contenente tutte le variabili catturate) e la tabella virtuale conterrebbe un puntatore all'implementazione del corrispondente tratto Fn*() generato dal compilatore il cui corpo sarebbe il corpo di chiusura.

Con le funzioni, tuttavia, le cose non sono così chiare. Non vi è alcun valore per creare un oggetto tratto da perché la funzione stessa dovrebbe corrispondere all'implementazione del tratto Fn(). Pertanto, rustc probabilmente genera una struttura vuota e implementa Fn() per esso, e questa implementazione chiama la funzione statica direttamente (non ruggine reale, ma qualcosa di simile):

struct SomeGeneratedStructFoo; 

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

Pertanto, quando un oggetto tratto viene creato dal fn foo() , un riferimento è in realtà un valore temporaneo di tipo SomeGeneratedStructFoo. Tuttavia, questo valore viene creato all'interno della corrispondenza e solo un riferimento ad esso viene restituito dalla corrispondenza, quindi questo valore non vive abbastanza a lungo, e questo è l'errore.

1

fn() è un tipo di puntatore a funzione. È già un tipo di puntatore. Puoi verificarlo con std::mem::size_of::<fn()>(). Non è un tipo di dimensioni zero.

Quando si esegue &foo, si prende un puntatore a un puntatore assegnato allo stack. Questo puntatore interno non sopravvive molto a lungo, causando l'errore.

È possibile trasmettere questi al tipo generico fn() come suggerito. Sarei interessato a sapere perché non puoi trasmettere fn() a &Fn(), però.

Problemi correlati