如何将通用T的实例转换为Rust中的具体实例?

时间:2018-02-22 07:34:02

标签: rust

我正在尝试实现这种模式:

use std::any::Any;
use std::fmt::Debug;

trait CommandHandler<TCommand> {
    fn execute(&self, data: TCommand);
}

#[derive(Debug)]
struct FooCommand {}

struct FooCommandHandler {}

impl CommandHandler<FooCommand> for FooCommandHandler {
    fn execute(&self, data: FooCommand) {
        println!("Foo");
    }
}

#[derive(Debug)]
struct BarCommand {}

struct BarCommandHandler {}

impl CommandHandler<BarCommand> for BarCommandHandler {
    fn execute(&self, data: BarCommand) {
        println!("Bar");
    }
}

fn execute<T>(command: T)
where
    T: Any + Debug,
{
    println!("Command: {:?}", command);
    match (&command as &Any).downcast_ref::<FooCommand>() {
        Some(c) => (FooCommandHandler {}).execute(c),
        None => {}
    };
    match (&command as &Any).downcast_ref::<BarCommand>() {
        Some(c) => (BarCommandHandler {}).execute(c),
        None => {}
    };
}

fn main() {
    (FooCommandHandler {}).execute(FooCommand {});
    (BarCommandHandler {}).execute(BarCommand {});
    execute(FooCommand {});
    execute(BarCommand {});
}

这不起作用:

error[E0308]: mismatched types
  --> src/main.rs:37:51
   |
37 |         Some(c) => (FooCommandHandler {}).execute(c),
   |                                                   ^ expected struct `FooCommand`, found &FooCommand
   |
   = note: expected type `FooCommand`
              found type `&FooCommand`

error[E0308]: mismatched types
  --> src/main.rs:41:51
   |
41 |         Some(c) => (BarCommandHandler {}).execute(c),
   |                                                   ^ expected struct `BarCommand`, found &BarCommand
   |
   = note: expected type `BarCommand`
              found type `&BarCommand`

如何以保留以下要求的方式实现execute()方法:

  • 类型XCommand应该完全天生执行它的XCommandHandler
  • 可能存在CommandHandler<X>的多个实现。
  • 命令处理程序接收(并使用)具体的命令实例,而不是对它的引用(不可能重复发送命令)。

本质上,我有一个泛型函数fn foo<T>(v: T),我想发送给一些具体的函数fn foo1(v: Foo)fn foo2(v: Bar);我该怎么做?

transmute是唯一的选择吗?

请注意,这与Any::downcast_ref所做的不同,后者从通用值v返回&Foo,而不是Foo

2 个答案:

答案 0 :(得分:3)

您需要通过Box,例如:

fn execute<T>(command: T)
where
    T: Any + Debug,
{
    println!("Command: {:?}", command);
    let any: Box<Any> = Box::new(command);

    let any = match any.downcast() {
        Ok(c) => return (FooCommandHandler {}).execute(*c),
        Err(any) => any,
    };

    let any = match any.downcast() {
        Ok(c) => return (BarCommandHandler {}).execute(*c),
        Err(any) => any,
    };

    let _ = any; // avoid unused variable error
    panic!("could not downcast command");
}

“但我使用Box!”

只需使用Box

“但这是一个分配!我已经测量了上面的代码并证明了这是一个瓶颈!”

什么? 真的?

“你无法证明其他。”

哦很好。但我保证这将在所有情况下都有效。这正在闯入“打击自己”的领域。除非您知道,否则不要这样做:

fn execute<T>(command: T)
where
    T: Any + Debug,
{
    use std::any::TypeId;
    use std::mem;

    println!("Command: {:?}", command);

    macro_rules! do_cast {
            ($t:ty, $h:expr) => {
                if TypeId::of::<T>() == TypeId::of::<$t>() {
                    let casted: $t = mem::transmute_copy(&command);
                    mem::forget(command); // we CANNOT let command drop.
                    $h.execute(casted);
                    return;
                }
            };
        }

    unsafe {
        do_cast!(FooCommand, FooCommandHandler {});
        do_cast!(BarCommand, BarCommandHandler {});
    }

    panic!("could not downcast command");
}

答案 1 :(得分:0)

正如接受答案的快速摘要:

&Any只有:

pub fn downcast_ref<T>(&self) -> Option<&T> where T: Any

Box<Any>实现:

pub fn downcast<T>(self) -> Result<Box<T>, Box<Any + 'static>> where T: Any

但是,由于复杂的原因,文档位于Box而不是Any