Ответ 1
То, что вы пытаетесь сделать, это вызвать закрытие из нескольких потоков. То есть, разделите закрытие на несколько потоков. Как только фраза "делиться несколькими потоками" пересекает мой разум, моя первая мысль для достижения Arc
(по крайней мере до RFC 458 реализуется в той или иной форме, когда &
станет пригодным для использования в потоках). Это позволяет использовать безопасную разделяемую память (она реализует Clone
, не требуя, чтобы ее внутренний тип был Clone
, так как Clone
просто создает новый указатель на одну и ту же память), и поэтому у вас может быть один объект Fn
, который используется в нескольких потоках, не нужно дублировать его.
В заключение поставьте WithCall
в Arc
и выполните клонирование.
#![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));
});
}
Примечание. Я удалил элемент unboxed_closures
с помощью рекомендуемого сахара Fn(...) -> ...
для типов и прямых вызовов ()
(не используя .call
). Кроме того, я удалил ненужный unsafe impl Send
, поскольку Send
автоматически реализуется, если содержимое. unsafe impl
требуются только в том случае, если по умолчанию не содержимое Send
, и программист хочет переопределить консервативное решение компилятора.
Старый ответ (это по-прежнему актуально): довольно необычно иметь объект-объект &mut Fn
, поскольку Fn::call
принимает &self
. mut
не требуется, и я думаю, что он добавляет буквально нулевую дополнительную функциональность. Наличие &mut Box<Fn()>
добавляет некоторые функции, но это также необычно.
Если вы перейдете на указатель &
вместо &mut
, все будет работать более естественно (с &Fn
и &Box<Fn>
). Не видя фактического кода, который вы используете, очень сложно точно сказать, что вы делаете, но
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));
}
(Отчасти это связано с тем, что &T
имеет значение Copy
, а также частично из-за переборки, он также работает с &mut
.)
В качестве альтернативы вы можете закрыть закрытие, которое, вероятно, работает в большем количестве ситуаций:
fn foo(f: &Fn()) {
something_else(|| f())
}
Очевидно, что FnMut не может быть клонирован по понятным причинам.
Нет никакой причины, по которой a FnMut
не может быть клонирован, это просто структура с некоторыми полями (и метод, который принимает &mut self
, а не &self
или self
как для Fn
и FnOnce
соответственно). Если вы создадите структуру и реализуете FnMut
вручную, вы все равно можете реализовать Clone
для нее.
Или безопасно каким-то образом передать необработанный указатель на Fn, например:
let func_pnt = &mut Box<Fn<...> + Send> as *mut Box<Fn<...>>
Технически вышеупомянутое работает, но кажется довольно странным.
Технически это работает, если вы будете осторожны, чтобы удовлетворить требования к псевдониму и пожизненному сроку работы Rust... но, обратившись к небезопасным указателям, вы ставите эту нагрузку на себя, не позволяя компилятору помочь вам. Сравнительно редко, что правильный ответ на ошибку компилятора заключается в использовании кода unsafe
, а не вникании в ошибку и настройке кода, чтобы сделать его более понятным (для компилятора, что часто приводит к тому, что он имеет больше смысла для людей).