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).
Cosa vorresti fare con la chiusura clonato? – Shepmaster
Qual è il tuo codice completo? – huon
@shepmaster Desidero in particolare clonare una chiusura senza stato mutabile per spostarla contemporaneamente in più attività. Guarda l'esempio che ho allegato. – Doug