我一定需要在这里装箱吗?

Do I necessarily need to Box here?

有没有什么方法可以在不使用 Boxing 的情况下使此代码工作:

fn some_func(my_type: MyType, some_str: &str) -> bool {
  let mut hmac = match my_type {
    MyType::MyType1 => create_hmac(Sha256::new(), some_str),
    MyType::MyType2 => create_hmac(Sha384::new(), some_str),
    MyType::MyType3 => create_hmac(Sha512::new(), some_str),
    _ => panic!()
  };

  //some calculations goes HERE, NOT in create_hmac function...
  hmac.input("fdsfdsfdsfd".to_string().as_bytes());

  //something else....
  true
}

fn create_hmac<D: Digest>(digest: D, some_str: &str) -> Hmac<D> {
  Hmac::new(digest, some_str.to_string().as_bytes())
}

它使用的库是 https://github.com/DaGenix/rust-crypto

您需要 Box 或使用引用,因为 "trait object" 只能在指针后面工作。

这是您的代码的一个非常简化的版本。你有三个不同的结构来实现相同的特征(摘要)

struct Sha256;
struct Sha384;
struct Sha512;

trait Digest {}
impl Digest for Sha256 {}
impl Digest for Sha384 {}
impl Digest for Sha512 {}

struct HMac<D: Digest> { d: D }

fn main() {
    let a = 1;

    // what you're trying to do
    // (does not work, Sha256, Sha384 and Sha512 are different types)
    //let _ = match a {
    //    1 => Sha256,
    //    2 => Sha384,
    //    3 => Sha512,
    //    _ => unreachable!()
    //};
}

请注意,在实际情况下,不仅所有 ShaXXX 类型的类型系统都不同,它们的内存布局也不同(比较 Engine256State with Engine512State for instance), so this rules out unsafe tricks with transmute

因此,如上所述,您可以使用 Box 或引用(但如果要使用引用,则必须在匹配之前预先创建一个具体实例):

fn main() {
    let a = 1;

    let _ : Box<Digest> = match a {
        1 => Box::new(Sha256),
        2 => Box::new(Sha384),
        3 => Box::new(Sha512),
        _ => unreachable!()
    };

    // to use references we need a pre-existing instance of all ShaXXX
    let (sha256, sha384, sha512) = (Sha256, Sha384, Sha512);

    let _ : &Digest = match a {
        1 => &sha256, //... otherwise the reference wouldn't outlive the match 
        2 => &sha384,
        3 => &sha512,
        _ => unreachable!()
    };
}

请注意,当您只想通过其接口使用对象时,Box 正在执行与大多数垃圾收集语言在后台为您执行的操作相同的操作。一些内存是为具体对象动态分配的,但实际上只允许传递指向内存的指针。

在你的情况下(但我还没有测试下面的代码)你应该能够做到:

//HMac implements a Mac trait, so we can return a Box<Mac>
// (I'm assuming you only want to use HMac through its Mac trait)
fn create_hmac<'a, D: Digest>(digest: D, some_str: &'a str) -> Box<Mac + 'a> {
  Box::new(Hmac::new(digest, some_str.to_string().as_bytes()))
}

并将其用作:

  let mut hmac: Box<Mac> = match my_type {
    MyType::MyType1 => create_hmac(Sha256::new(), some_str),
    MyType::MyType2 => create_hmac(Sha384::new(), some_str),
    MyType::MyType3 => create_hmac(Sha512::new(), some_str),
    _ => unreachable!()
  };

对 Paolo 的正确回答进行补充和澄清。首先,您可以使您的枚举包含适当的 Sha* 结构,然后通过适当地委托来实现 Digest 。这可能并非在所有情况下都有意义,但如果从概念上讲这就是您正在做的事情,那么这可能是有意义的:

struct Sha256;
struct Sha384;
struct Sha512;

trait Digest { fn digest(&self); }
impl Digest for Sha256 { fn digest(&self) {println!("256")} }
impl Digest for Sha384 { fn digest(&self) {println!("384")} }
impl Digest for Sha512 { fn digest(&self) {println!("512")} }

enum MyType {
    One(Sha256),
    Two(Sha384),
    Three(Sha512),
}

impl Digest for MyType {
    fn digest(&self) {
        use MyType::*;

        match *self {
            One(ref sha)   => sha.digest(),
            Two(ref sha)   => sha.digest(),
            Three(ref sha) => sha.digest(),
        }
    }
}

fn main() {
    let a = MyType::Two(Sha384);
    a.digest()
}

此外,如果要使用引用,则不必实际实例化 所有 类型,只需确保您使用的类型可用即可。您还必须有引用可以超出 match 表达式的地方:

#![feature(std_misc)]
#![feature(io)]

use std::time::duration::Duration;
use std::old_io::timer::sleep;

struct Sha256(u8);
struct Sha384(u8);
struct Sha512(u8);

impl Sha256 { fn new() -> Sha256 { sleep(Duration::seconds(1)); Sha256(1) }}
impl Sha384 { fn new() -> Sha384 { sleep(Duration::seconds(2)); Sha384(2) }}
impl Sha512 { fn new() -> Sha512 { sleep(Duration::seconds(3)); Sha512(3) }}

trait Digest {}
impl Digest for Sha256 {}
impl Digest for Sha384 {}
impl Digest for Sha512 {}

fn main() {
    let a = 1;

    let sha256: Sha256;
    let sha384: Sha384;
    let sha512: Sha512;

    let _ : &Digest = match a {
        1 => {
            sha256 = Sha256::new();
            &sha256
        },
        2 => {
            sha384 = Sha384::new();
            &sha384
        },
        3 => {
            sha512 = Sha512::new();
            &sha512
        },
        _ => unreachable!()
    };
}