2017-01-15 8 views
2

Я немного зациклен на том, как заставить это работать, я отрезал его от реальной вещи. Я написал черту:Передача метода закрытия: метод ожидаемого типа, найдено закрытие

pub trait Renderable<F: Fn(&PropertyTags)> { 
    fn set_property_changed_callback(&mut self, callback: Option<F>); 
} 

Какой параметр «ребенок» из add_child ограничена из и PropertyTags это просто перечисление. Я включил имитировали реализации типа child, чтобы продемонстрировать свое использование:

pub struct Child<F: Fn(&PropertyTags)> { 
    property_changed_callback: Option<F>, 
} 

impl<F: Fn(&PropertyTags)> Renderable<F> for Child<F> { 
    fn set_property_changed_callback(&mut self, callback: Option<F>) { 
     self.property_changed_callback = callback; 
    } 
} 

Тогда они будут использоваться как:

pub fn add_child<REND, C>(&mut self, child: &mut REND) 
    where C: Fn(&PropertyTags), 
     REND: Renderable<C> 
{ 
    let tc = Some(|property_tag: &PropertyTags|{ 
      }); 

    child.set_property_changed_callback(tc); 
} 

я получаю ошибку:

child.set_property_changed_callback(tc); 
    |        ^^ expected type parameter, found closure 
    | 
    = note: expected type `std::option::Option<C>` 
    = note: found type `std::option::Option<[[email protected]/rendering/mod.rs:74:31: 76:18]>` 
    = help: here are some functions which might fulfill your needs: 
- .take() 
- .unwrap() 

Я установил минимальный пример игровой площадки, где воспроизводятся следующие проблемы: https://play.rust-lang.org/?gist=bcc8d67f25ac620fe062032d8737954b&version=stable&backtrace=0

+0

Прошу прощения, если это дубликат, я прочитал кучу подобных вопросов, но ни один из них не помог мне исправить это. –

+0

Вам нужно будет добавить больше кода, чтобы мы могли его протестировать. А пока вы видели [этот вопрос] (http://stackoverflow.com/questions/36414576/returning-a-closure-from-a-trait-method-involving-generics-in-rust)? Вероятно, вам нужно закрыть закрытие в «Коробке». – ljedrz

+0

Я не был уверен, что это была та же проблема, поскольку они пытались вернуть закрытие. Я надеялся избежать бокса, я был под впечатлением, что это сделало бы его динамически отправленным? –

ответ

3

Проблема заключается в том, что add_child требования принять любой Renderable<C>, где C может быть любого типа, который реализует Fn(&PropertyTags), но тогда функция пытается придать ему определенный тип закрытия, что не может быть таким же, как C.

Для того, чтобы это работало, подпись add_child «s должен выглядеть следующим образом:

pub fn add_child<REND>(&mut self, child: &mut REND) 
    where REND: Renderable<AddChildCallback> 

где AddChildCallback это имя конкретного типа (который реализует Fn(&PropertyTags)).

Трудность здесь в том, что с одной стороны, типы закрытия не имеют имени, которое вы можете использовать в коде Rust, а с другой стороны, реализация Fn вручную нестабильна, поэтому для нее требуется ночной компилятор.

Я также хотел бы отметить, что если тип обратного вызова является параметром типа, то после первого обратного вызова нельзя назначить обратный вызов другого типа, поскольку первый обратный вызов определит конкретный тип для Renderable , Это может быть хорошо для вашего использования, я просто хотел убедиться, что вы об этом знаете.

Если вы хотите решение, которое работает на стабильных компиляторах (начиная с Rust 1.14.0), тогда вам нужно будет установить обратный вызов. подпись add_child «s будет выглядеть следующим образом:

pub fn add_child<REND>(&mut self, child: &mut REND) 
    where REND: Renderable<Box<Fn(&PropertyTags)>> 

Here обновленная площадка ссылка с примером реализации Fn. Обратите внимание, что параметры для call, call_mut и call_once передаются в виде кортежа, как того требует определение признака. Код воспроизводится ниже для полноты:

struct RenderableCallback { 

} 

impl<'a> Fn<(&'a PropertyTags,)> for RenderableCallback { 
    extern "rust-call" fn call(&self, args: (&'a PropertyTags,)) -> Self::Output { 

    } 
} 

impl<'a> FnMut<(&'a PropertyTags,)> for RenderableCallback { 
    extern "rust-call" fn call_mut(&mut self, args: (&'a PropertyTags,)) -> Self::Output { 

    } 
} 

impl<'a> FnOnce<(&'a PropertyTags,)> for RenderableCallback { 
    type Output =(); 
    extern "rust-call" fn call_once(self, args: (&'a PropertyTags,)) -> Self::Output { 
    } 
} 
+0

Чтобы уточнить, можно ли назначить другой AddChildCallback, но не другую конкретную реализацию Fn (& PropertyTags)? –

+0

Это правильно. –

 Смежные вопросы

  • Нет связанных вопросов^_^