【问题标题】:How to overcome type change for function argument when moving it to HashMap?将函数参数移动到 HashMap 时如何克服函数参数的类型更改?
【发布时间】:2022-12-06 16:19:44
【问题描述】:

以下代码描述了使用 HashMap 准备异步调用的容器:

use std::{
    collections::HashMap,
    any::Any,
    marker::Send
};
use futures::future::{Future, BoxFuture};

// ===================================

pub type AnyType = Box<dyn Any + Send>;

// ===================================

trait AsyncFn {
    fn call(&self, arg: AnyType) -> BoxFuture<'static, ()>;
}

impl<T, F> AsyncFn for T
where
    T: Fn(AnyType) -> F,
    F: Future<Output = ()> + 'static + Send,
{
    fn call(&self, arg: AnyType) -> BoxFuture<'static, ()> {
        Box::pin(self(arg))
    }
}

async fn async_test(data: AnyType) -> () {
    let d: Box<String> = data.downcast().unwrap();
    println!("String data = {d:?}");
}

#[async_std::main]
async fn main() {
    let mut callables: HashMap<String, Box<dyn AsyncFn>> = HashMap::new();
    callables.insert(
        "test_func".to_string(),
        Box::new(async_test)
    );

    let awaitable = callables.get("test_func").unwrap();
    awaitable.call(Box::new("test string argument1".to_string())).await;
}

我面临着以这种方式准备未来调用的任务,不仅是一个异步函数,而且还有一个额外的相关参数。我试着这样做:

// ...
pub type AnyBindType = Option<AnyType>;

// ===================================

trait AsyncBindFn {
    fn call(&self, arg: AnyBindType) -> BoxFuture<'static, ()>;
}

impl<T, F> AsyncBindFn for T
where
    T: Fn(AnyBindType) -> F,
    F: Future<Output = ()> + 'static + Send,
{
    fn call(&self, arg: AnyBindType) -> BoxFuture<'static, ()> {
        Box::pin(self(arg))
    }
}

async fn async_test2(data: AnyBindType) -> () {
    if let Some(ref d) = data {
        let d = d.downcast_ref::<String>();
        println!("String data = {d:?}");
    }
}

#[async_std::main]
async fn main() {
    // ...

    let mut bind_callables: HashMap<String, (Box<dyn AsyncBindFn>, AnyBindType)> = HashMap::new();
    bind_callables.insert(
        "bind_test_func".to_string(),
        (Box::new(async_test2), Some(Box::new("test bind string argument1".to_string())))
    );

    let bind_awaitable_data = bind_callables.get("bind_test_func").unwrap();
    let (bind_awaitable, bind_arg) = bind_awaitable_data;

    // if let Some(ref d) = bind_arg {
    //     let d = d.downcast_ref::<String>();
    //     println!("String data = {d:?}");
    // }

    bind_awaitable.call(bind_arg).await; // ! ERROR
    //                  ^^^^^^^^ - mismatched type
}

数据传输到 HashMap 后,获取原始数据的唯一方法是对其执行 remove(),但我需要能够重用它。意思是,我需要通过 ref 访问。

在最后一个示例中,注释掉的if let 的最后一个块成功打印了此数据,但由于它是一个引用,我无法将其发送到相应的函数中执行相同的操作,因为它是类型不匹配。

因此,签名的描述应该相应地更改,以便该函数引用一个可选参数,但是当我收到它时,我将有一个对可选数据的引用,等等......

如何克服这种情况?

【问题讨论】:

    标签: rust


    【解决方案1】:

    如果您想将对实现 Any 特性和 Send 标记特性的类型的引用传递给函数,您可以使用 std::any::Any 特性的 downcast_ref 方法来尝试将引用向下转换为您指定的具体类型可以使用。如果向下转型成功,此方法将返回 Some,否则将返回 None。

    以下是如何在代码中使用 downcast_ref 方法的示例:

    #[async_std::main]
    

    异步 fn main() { 让 mut bind_callables: HashMap<String, (Box, AnyBindType)> = HashMap::new(); bind_callables.insert( “bind_test_func”.to_string(), (Box::new(async_test2), Some(Box::new("test bind string argument1".to_string()))) );

    let bind_awaitable_data = bind_callables.get("bind_test_func").unwrap();
    let (bind_awaitable, bind_arg) = bind_awaitable_data;
    
    if let Some(d) = bind_arg.as_ref().and_then(|d| d.downcast_ref::<String>()) {
        println!("String data = {}", d);
    }
    
    bind_awaitable.call(bind_arg).await;
    

    }

    在这个例子中,我们使用as_ref方法将bind_arg中的Option值转换为一个引用,然后使用and_then方法将downcast_ref方法应用到这个引用上。如果 downcast_ref 方法返回 Some,我们打印它包含的值。

    最后,我们将 bind_arg 值传递给 AsyncBindFn 特性实现的调用方法。因为 bind_arg 是 Option 类型,调用方法将能够接受它而不会出现任何类型错误。

    我希望这有帮助!如果您有任何其他问题,请告诉我。

    【讨论】:

      猜你喜欢
      • 2017-05-02
      • 1970-01-01
      • 2019-03-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-28
      • 2020-11-28
      相关资源
      最近更新 更多