是否可以在不同的结构中存储包含闭包的Rust结构?

taz*_*jin 5 traits rust

CRIUS库提供断路器样功能生锈.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>,
}
Run Code Online (Sandbox Code Playgroud)

是否可以将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));
}
Run Code Online (Sandbox Code Playgroud)

为函数编写返回类型时变得更加困难:

fn return_command_instance() -> Command<u8, u8, ???> {
                                                ^
                                                |
                          What goes here? -------

    Command::define(|n: u8| Ok(n * 2))
}
Run Code Online (Sandbox Code Playgroud)

编译器推断的类型是匿名的 - 它不能放在那里.很多时候,当关闭传递时,人们会使用a Box<F: Fn<...>>,但是没有实现 impl Fn<T> for Box<Fn<T>>- 所以拳击类型打破了给出的约束crius::command::Command.

在具有新impl Trait功能的Rust版本中(例如即将推出的稳定版本),这是可能的:

/// 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))
}
Run Code Online (Sandbox Code Playgroud)

这在稳定的Rust中不起作用,impl Trait只能在返回类型中使用,而不能在struct成员中使用.

尝试传播泛型类型最终看起来像这样:

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))
}
Run Code Online (Sandbox Code Playgroud)

但这不编译:

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]`
Run Code Online (Sandbox Code Playgroud)

同样,我不知道在结果签名中指定具体类型的方法.


即使将类型作为通用参数传播起作用,对于我们的特定用例仍然是一个问题.我们希望存储一个 Command作为actix actor的一部分,它注册为a SystemService,这需要一个Default实现,这最终迫使我们提供一个具体的类型.

如果有人对可能的方法有任何想法,请分享.当然知道它是不是有可能也将是不错.

Luk*_*oid 5

我目前知道闭包不能用作返回类型的一部分,除了使用implor之外Box,你提到过这两种情况都不能在这种情况下使用.

另一种方法是使用函数指针而不是闭包,如下所示:

fn return_command_instance() -> Command<u8, u8, fn(u8) -> Result<u8, Box<CommandError>>> {
    Command::define(|n: u8| Ok(n * 2))
}
Run Code Online (Sandbox Code Playgroud)

注意小写fn表示函数指针而不是特征Fn.有关高级功能和闭包的章节将对此进行更详细的说明.

这只有在你没有捕获函数中的任何变量时才会起作用,如果这样做,它将被编译成一个闭包.