是否可以在不同的结构中存储包含闭包的 Rust 结构? [英] Is it possible to store a Rust struct containing a closure in a different struct?

查看:55
本文介绍了是否可以在不同的结构中存储包含闭包的 Rust 结构?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Crius 库提供Rust 的类似断路器的功能.Crius 定义了一个名为 Command 看起来像这样:

The Crius library provides circuit-breaker-like functionality for Rust. Crius defines a struct called Command which looks like this:

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 的实例存储为不同结构中的字段?

Is it possible to store an instance of Command as a field in a different struct?

我开始尝试从一个功能.简单地实例化类型是没有问题的:

I started out trying to return a value of this type from a function. Simply instantiating the type is no problem:

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

为返回类型编写返回类型变得更加困难功能:

It becomes more difficult when writing a return type for the function:

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

    Command::define(|n: u8| Ok(n * 2))
}

编译器推断的类型是匿名的——不能放入那里.很多时候,当关闭被传递时,人们诉诸于使用 Box>,但是没有实现impl Fn对于 Box<Fn<T>> - 所以装箱类型打破crius::command::Command 给出的约束.

The type inferred by the compiler is anonymous - it can't be put in there. Many times when closures are passed around, people resort to using a Box<F: Fn<...>>, however there is no implementation for impl Fn<T> for Box<Fn<T>> - so boxing the type breaks the constraints given by crius::command::Command.

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

In versions of Rust that have the new impl Trait feature (such as the upcoming stable release), this is possible:

/// 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 只能用于返回类型,而不是结构成员.

This does not work in stable Rust and impl Trait can only be used in return types, not in struct members.

试图传播泛型类型最终看起来像这个:

Trying to propagate the generic type ends up looking something like this:

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]`

同样,我不知道有什么方法可以在结果签名.

Again, I don't know of a way to specify that concrete type in the result signature.

即使将类型作为泛型参数传播有效,它也会对于我们的特定用例仍然是一个问题.我们要存储一个Command 作为 actix 参与者的一部分,注册为SystemService,需要一个 Default 实现,它再次最终迫使我们提供一个具体的类型.

Even if propagating the type as a generic parameter worked, it would still be an issue for our specific use-case. We want to store a Command as part of an actix actor which registers as a SystemService, which requires a Default implementation, which again eventually forces us to provide a concrete type.

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

If anyone has any ideas about possible ways to do this, please share them. Definitely knowing that it isn't possible would also be nice.

推荐答案

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

I currently know of no way a closure may be used as part of a return type other than using impl or Box, both of which you have mentioned and cannot be used in this situation.

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

An alternative would be to use a function pointer instead of a closure, like so:

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

注意小写的 fn 表示函数指针而不是特征 Fn.这在 高级功能&关闭.

Notice the lower case fn to signify a function pointer and not the trait Fn. This is explained in more details in the chapter on Advanced Functions & Closures.

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

This will only work if you do not capture any variables in the function, if you do it will be compiled into a closure.

这篇关于是否可以在不同的结构中存储包含闭包的 Rust 结构?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆