Подтвердить что ты не робот

Можете ли вы клонировать закрытие?

FnMut не может быть клонирован по понятным причинам.

Однако Fn имеет непреложный объем; есть ли способ создать "дубликат" Fn?

Попытка клонирования приводит к:

&mut core::ops::Fn(logger::Level, &'a collections::string::String) + Send не реализует какой-либо метод в области с именем clone

Или безопасно каким-то образом передать необработанный указатель на Fn, например:

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

Технически вышеупомянутое работает, но кажется довольно странным.

Humm, хорошо, вот пример того, что я пытаюсь сделать:

#![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));
  });
}

т. У вас есть структура с коротким замыканием в ней, вам нужно передать эту структуру на ряд задач; очевидно, вы не можете.... но вы также не можете клонировать его, потому что вы не можете клонировать Box<Fn<>>, и вы не можете клонировать &Fn<...>

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

4b9b3361

Ответ 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));
    });
}

playpen

Примечание. Я удалил элемент 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, а не вникании в ошибку и настройке кода, чтобы сделать его более понятным (для компилятора, что часто приводит к тому, что он имеет больше смысла для людей).

Ответ 2

Помните, что закрытие захватывает их среду, поэтому у них есть своя жизнь, основанная на среде. Однако вы можете взять ссылки на Fn* и передать их дальше или сохранить в структуре:

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

Я бы сказал, что небезопасно преобразовать Fn* в необработанный указатель и передать его из-за проблем с продолжительностью жизни.

Ответ 3

Вот рабочий код в 1.22.1

Цель состоит в том, чтобы сделать эту работу.

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

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

y.clone();

Использовался исходный код, предложенный сверху.

Я начал с клонирования закрытия Fn.

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

Закончено добавление Arc вокруг Fp в struct WithCall

Воспроизведение ржавчины: рабочий код Gist: рабочий код в 1.22.1