2015-11-11 14 views
8

Sto cercando di capire come inviare una funzione attraverso un canale e come evitare la clonazione extra per eseguire la funzione dall'altra parte. Se rimuovo l'operazione in più di clonazione all'interno della chiusura, ottengo il seguente errore:Impossibile spostare la variabile esterna catturata in una chiusura `Fn`

error: cannot move out of captured outer variable in an 'Fn' closure 

Ignorando il fatto che questo codice non fa assolutamente nulla, e si avvale di un globale mutabile statica Sender<T>, rappresenta quello che sto cercando di ottenere dando gli errori del compilatore corretti. Questo codice è non destinato a essere eseguito, appena compilato.

use std::ops::DerefMut; 
use std::sync::{Arc, Mutex}; 
use std::collections::LinkedList; 
use std::sync::mpsc::{Sender, Receiver}; 

type SafeList = Arc<Mutex<LinkedList<u8>>>; 
type SendableFn = Arc<Mutex<(Fn() + Send + Sync + 'static)>>; 
static mut tx: *mut Sender<SendableFn> = 0 as *mut Sender<SendableFn>; 

fn main() { 
    let list: SafeList = Arc::new(Mutex::new(LinkedList::new())); 
    loop { 
     let t_list = list.clone(); 
     run(move || { 
      foo(t_list.clone()); 
     }); 
    } 
} 

fn run<T: Fn() + Send + Sync + 'static>(task: T) { 
    unsafe { 
     let _ = (*tx).send(Arc::new(Mutex::new(task))); 
    } 
} 

#[allow(dead_code)] 
fn execute(rx: Receiver<SendableFn>) { 
    for t in rx.iter() { 
     let mut guard = t.lock().unwrap(); 
     let task = guard.deref_mut(); 
     task(); 
    } 
} 

#[allow(unused_variables)] 
fn foo(list: SafeList) { } 

C'è un metodo migliore per aggirare quell'errore e/o un altro modo in cui dovrei inviare le funzioni attraverso i canali?

risposta

11

Il problema con Fn() è che è possibile chiamarlo più volte. Se ti sei spostato da un valore acquisito, quel valore non sarebbe più disponibile alla prossima chiamata. Hai bisogno di un FnOnce() per assicurarti che chiamare anche la chiusura si sposti, quindi è andato e non può essere chiamato di nuovo.

Non c'è modo di avere un Arc<Mutex<(FnOnce() + Send + Sync + 'static)>>. Ciò richiederebbe di nuovo che garantiate staticamente che dopo aver chiamato la funzione, nessun altro potrà richiamarla di nuovo. Quale non è possibile, poiché qualcun altro potrebbe avere un altro Arc che punta al tuo FnOnce. Quello che puoi fare è inserirlo e inviarlo come Box<FnOnce() + Send + Sync + 'static>. C'è sempre un solo proprietario di un Box.

Il problema con FnOnce() è che non si può realmente chiamarlo mentre è nello Box, perché ciò richiederebbe di spostarlo dallo Box e chiamarlo. Ma non ne conosciamo le dimensioni, quindi non possiamo spostarlo dallo Box. In futuro le chiusure Box<FnOnce()> potrebbero diventare direttamente utilizzabili.

"Fortunatamente" questo problema si è verificato più spesso, quindi c'è FnBox. Purtroppo questo richiede che la notte funzioni. Inoltre, non sono riuscito a capire come utilizzare la sintassi di chiamata della funzione descritta nei documenti, ma è possibile chiamare manualmente call_box su Box<FnBox()>. Provalo nel Playground

+0

Sfortunatamente, la mia unica opzione è stabile. Sembra che tu abbia appena confermato ciò che ho già provato e capito, ma qualcun altro potrebbe trovare questo super-aiuto che ha la capacità di usarlo ogni notte. Grazie :) – nathansizemore

+1

Puoi usare un trucco. Usi ancora un 'Fn' e muovi solo in' Option's con cui esci con 'take'. Quindi si verifica un errore di runtime quando si usa male il 'Fn' –

+0

Cosa succede se ho bisogno di un' Fn', a causa di un requisito di libreria. –