Библиотека Crius предоставляет
функциональность, подобная выключателю, для Rust. Crius определяет структуру с именем Command
, которая выглядит следующим образом:
pub struct Command<P, T, CMD>
where
T: Send,
CMD: Fn(P) -> Result<T, Box<CommandError>> + Sync + Send,
{
pub config: Option<Config>,
pub cmd: CMD,
phantom_data: PhantomData<P>,
}
Можно ли сохранить экземпляр Command
как поле в другой структуре?
Я начал пытаться вернуть значение этого типа из
функция. Простое создание экземпляра типа не проблема:
/// This function constructs a simple instance of `Command<P, T, CMD>` with the
/// types set to:
///
/// P ~ u8
/// T ~ u8
/// CMD: Fn(u8) -> Result<u8, Box<CommandError>> + Send + Sync
///
/// This function compiles fine. However, there is no *concrete* type
/// for `CMD`. In compiler output it will be referred to as an
/// "anonymous" type looking like this:
///
/// Command<u8, u8, [closure@src/lib.rs:19:21: 19:38]>
fn simple_command_instance() {
let _ = Command::define(|n: u8| Ok(n * 2));
}
Это становится более трудным при написании типа возврата для
Функция:
fn return_command_instance() -> Command<u8, u8, ???> {
^
|
What goes here? -------
Command::define(|n: u8| Ok(n * 2))
}
Тип, выведенный компилятором, является анонимным - его нельзя поместить в
там. Много раз, когда замыкания проходят вокруг, люди прибегают к
используя Box<F: Fn<...>>
, однако реализации для
impl Fn<T> for Box<Fn<T>>
- так бокс типа ломает
ограничения, заданные crius::command::Command
.
В версиях Rust с новой функцией impl Trait
(например,
грядущая стабильная версия), это возможно:
/// Use new `impl Trait` syntax as a type parameter in the return
/// type:
fn impl_trait_type_param() -> Command<u8, u8, impl Fn(u8) -> Result<u8, Box<CommandError>>> {
Command::define(|n: u8| Ok(n * 2))
}
Это не работает в стабильном Rust, и impl Trait
может только
использоваться в возвращаемых типах, а не в членах структуры.
Попытка распространения универсального типа выглядит примерно так
это:
fn return_cmd_struct<F>() -> Command<u8, u8, F>
where
F: Fn(u8) -> Result<u8, Box<CommandError>> + Send + Sync,
{
Command::define(|n: u8| Ok(n * 2))
}
Но это не компилируется:
error[E0308]: mismatched types
--> src/lib.rs:33:21
|
33 | Command::define(|n: u8| Ok(n * 2))
| ^^^^^^^^^^^^^^^^^ expected type parameter, found closure
|
= note: expected type `F`
found type `[closure@src/lib.rs:33:21: 33:38]`
Опять же, я не знаю способа указать этот конкретный тип в
подпись результата.
Даже если распространение типа в качестве общего параметра сработало, это
все еще будет проблемой для нашего конкретного варианта использования. Мы хотим хранить
Command
как часть actix актера, который регистрируется как
SystemService
, что требует реализации Default
, которая
снова в конечном итоге заставляет нас предоставить конкретный тип.
Если у кого-нибудь есть идеи о возможных способах сделать это, пожалуйста, поделитесь
их. Определенно знать, что это не возможно, было бы неплохо.