2015-01-11 20 views
12

Un FnMut non può essere clonato, per ovvi motivi.Puoi clonare una chiusura?

Tuttavia, un Fn ha una portata immutabile; c'è un modo per creare un "duplicato" di un Fn?

Cercando di clonare il risultato e ':

&mut core::ops::Fn(logger::Level, &'a collections::string::String) + Send does not implement any method in scope named clone

O è sicuro passare in qualche modo un puntatore prima per un Fn giro, come:

let func_pnt = &mut Box<Fn<...> + Send> as *mut Box<Fn<...>> 

Tecnicamente le opere di cui sopra, ma sembra piuttosto strano

Humm, ok, ecco un esempio di quello che sto cercando di fare:

#![feature(unboxed_closures)] 

use std::thread::Thread; 
use std::clone::Clone; 

struct WithCall { 
    fp:Box<Fn<(i8,i8), i8> + Send> 
} 

impl WithCall { 
    pub fn new(fp:Box<Fn<(i8,i8), i8> + Send>) -> WithCall { 
    return WithCall { 
     fp: fp 
    }; 
    } 

    pub fn run(&self, a:i8, b:i8) -> i8 { 
    return self.fp.call((a, b)); 
    } 
} 

unsafe impl Send for WithCall {} 

impl Clone for WithCall { 
    fn clone(&self) -> WithCall { 
    return WithCall { 
     fp: self.fp.clone() 
    }; 
    } 
} 

fn main() { 
    let adder = WithCall::new(Box::new(|&:a:i8, b:i8| -> i8 { 
    return a + b; 
    })); 
    println!("{}", adder.run(1, 2)); 

    let add_a = adder.clone(); 
    let add_b = adder.clone(); 
    Thread::scoped(move || { 
    println!("In remote thread: {}", add_a.run(10, 10)); 
    }); 
    Thread::scoped(move || { 
    println!("In remote thread: {}", add_b.run(10, 10)); 
    }); 
} 

es. Hai una struttura con una chiusura in scatola, devi passare quella struttura ad un numero di compiti; ovviamente non puoi ... ma anche voi non può clonare, perché non è possibile clonare un Box<Fn<>> e non si può clonare un &Fn<...>

box: http://is.gd/1oNPYJ

+0

Cosa vorresti fare con la chiusura clonato? – Shepmaster

+0

Qual è il tuo codice completo? – huon

+0

@shepmaster Desidero in particolare clonare una chiusura senza stato mutabile per spostarla contemporaneamente in più attività. Guarda l'esempio che ho allegato. – Doug

risposta

1

Ricordate che le chiusure catturare il loro ambiente, in modo da avere un vita propria, basata sull'ambiente. Tuttavia, è possibile prendere i riferimenti alla Fn* e superare quelli intorno ulteriormente, o memorizzarli in una struttura:

fn do_more<F>(f: &F) -> u8 
    where F: Fn(u8) -> u8 
{ 
    f(0) 
} 

fn do_things<F>(f: F) -> u8 
    where F: Fn(u8) -> u8 
{ 
    // We can pass the reference to our closure around, 
    // effectively allowing us to use it multiple times. 
    f(do_more(&f)) 
} 

fn main() { 
    let val = 2; 
    // The closure captures `val`, so it cannot live beyond that. 
    println!("{:?}", do_things(|x| (x + 1) * val)); 
} 

direi che non è universalmente sicuro per convertire il Fn* a un puntatore prime e passarlo intorno , a causa dei problemi di durata.

11

Quello che stai cercando di fare è chiamare una chiusura da più thread. Cioè, condividi la chiusura su più thread. Non appena la frase "condividi su più thread" mi passa per la testa, il mio primo pensiero è to reach for Arc (almeno fino al RFC 458 è implementato in qualche modo, quando & diventerà utilizzabile tra i thread). Ciò consente una memoria condivisa sicura (implementa Clone senza richiedere che il suo tipo interno sia Clone, poiché Clone crea semplicemente un nuovo puntatore alla stessa memoria) e quindi è possibile avere un singolo oggetto Fn che viene utilizzato in più thread, non è necessario duplicarlo.

In sintesi, inserisci il tuo WithCall in un Arc e clonalo.

#![allow(unstable)] 

use std::thread::Thread; 
use std::sync::Arc; 

type Fp = Box<Fn(i8,i8) -> i8 + Send + Sync>; 

struct WithCall { 
    fp: Fp 
} 

impl WithCall { 
    pub fn new(fp: Fp) -> WithCall { 
     WithCall { fp: fp } 
    } 

    pub fn run(&self, a: i8, b: i8) -> i8 { 
     (*self.fp)(a, b) 
    } 
} 

fn main() { 
    let adder = WithCall::new(Box::new(|&: a: i8, b| a + b)); 
    println!("{}", adder.run(1, 2)); 

    let add_a = Arc::new(adder); 
    let add_b = add_a.clone(); 
    Thread::scoped(move || { 
     println!("In remote thread: {}", add_a.run(10, 10)); 
    }); 
    Thread::scoped(move || { 
     println!("In remote thread: {}", add_b.run(10, 10)); 
    }); 
} 

playpen

Nota: Ho rimosso il cancello unboxed_closures funzionalità utilizzando lo zucchero consigliata Fn(...) -> ... per tipi e () chiamate dirette (se non utilizzano .call). Inoltre, ho rimosso l'inutile unsafe impl Send, poiché Send viene implementato automaticamente se il contenuto lo è. unsafe impl s sono richiesti solo se il contenuto non è Send per impostazione predefinita e il programmatore desidera ignorare il giudizio conservativo del compilatore.


Vecchio risposta (questo è ancora rilevante): E 'abbastanza insolito avere un oggetto &mut Fn tratto, dal momento che Fn::call prende &self. Il numero mut non è necessario e penso che aggiunga letteralmente zero funzionalità extra. Avere un &mut Box<Fn()> aggiunge alcune funzionalità, ma è anche insolito.

Se si passa a un puntatore invece di un &mut cose & funzionerà in modo più naturale (sia con &Fn e &Box<Fn>). Senza vedere il codice vero e proprio che si sta utilizzando, è estremamente difficile dire esattamente quello che stai facendo, ma

fn call_it(f: &Fn()) { 
    (*f)(); 
    (*f)(); 
} 
fn use_closure(f: &Fn()) { 
    call_it(f); 
    call_it(f); 
} 

fn main() { 
    let x = 1i32; 
    use_closure(&|| println!("x is {}", x)); 
} 

(Ciò è in parte dovuto al &T essendo Copy e in parte anche a causa di reborrowing, ma funziona con &mut . oltre)

in alternativa, è possibile chiudere-over della chiusura, che probabilmente opera in più situazioni:

fn foo(f: &Fn()) { 
    something_else(|| f()) 
} 

Obviously a FnMut cannot be cloned, for obvious reasons.

Non esiste un motivo intrinseco a FnMut non può essere clonato, è solo una struttura con alcuni campi (e un metodo che prende &mut self, anziché &self o self come per Fn e FnOnce rispettivamente). Se si crea una struct e si implementa manualmente FnMut, è ancora possibile implementare Clone per esso.

Or is it safe to somehow pass a raw pointer to a Fn around, like:

let func_pnt = &mut Box<Fn<...> + Send> as *mut Box<Fn<...>> 

Technically the above works, but it seems quite weird.

Tecnicamente funziona se siete attenti a garantire i requisiti di aliasing e la durata della ruggine sono soddisfatti ... ma optando per i puntatori non sicuri che stai mettendo tale onere su te stesso, non lasciare che l'aiuto compilatore tu. È relativamente raro che la risposta corretta a un errore del compilatore sia quella di utilizzare il codice unsafe piuttosto che scavare nell'errore e modificare il codice per renderlo più sensato (per il compilatore, che spesso risulta più sensato per gli utenti).

+0

Scusa, questa era una domanda un po 'spazzatura. L'ho aggiornato con un esempio di ciò che sto cercando di fare. Chiusura di una chiusura potrebbe essere una soluzione, ma non sono sicuro di come lo faresti. – Doug

+0

@Doug, sì, le domande senza la fonte effettiva sono estremamente difficili da rispondere in modo utile. Aggiornato. – huon

0

Ecco il codice che lavora in 1.22.1

L'intento è quello di rendere questo lavoro.

let x = |x| { println!("----{}",x)}; 

let mut y = Box::new(x); 

y.clone(); 

È stato utilizzato il codice originale come suggerito nella parte superiore.

Ho iniziato con la clonazione di una chiusura Fn.

type Fp = Box<Fn(i8, i8) -> i8 + Send + Sync>; 

finito per l'aggiunta di Arc intorno Fp nella struct WithCall

Play rust : working code Gist : working code in 1.22.1

Problemi correlati