【问题标题】:Is it possible to store a Rust struct containing a closure in a different struct?是否可以将包含闭包的 Rust 结构存储在不同的结构中?
【发布时间】:2018-05-09 20:28:48
【问题描述】:

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&lt;F: Fn&lt;...&gt;&gt;,但是没有实现 impl Fn&lt;T&gt; for Box&lt;Fn&lt;T&gt;&gt; - 所以装箱类型会破坏 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))
}

这在稳定的 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 实现,它 最终再次迫使我们提供具体类型。

如果有人对可能的方法有任何想法,请分享 他们。绝对知道它不可能也很好。

【问题讨论】:

    标签: rust traits


    【解决方案1】:

    我目前知道除了使用 implBox 之外,没有任何其他方法可以将闭包用作返回类型的一部分,您已经提到了这两种方法,但不能在这种情况下使用。

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

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

    注意小写fn 表示函数指针,而不是特征Fn。这在Advanced Functions & Closures一章中有更详细的解释。

    这只有在你不捕获函数中的任何变量时才有效,如果你这样做了,它将被编译成一个闭包。

    【讨论】:

    • 这似乎解决了我的问题,谢谢指出!在重新阅读了一些crius 源代码后,我还注意到它在某些地方内部使用了函数指针类型。
    猜你喜欢
    • 2020-07-18
    • 2015-03-06
    • 2021-09-05
    • 1970-01-01
    • 1970-01-01
    • 2017-04-19
    • 2016-10-17
    相关资源
    最近更新 更多