有没有办法让这段代码在不使用Box
的情况下运行:
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())
}
答案 0 :(得分:3)
您需要Box或使用引用,因为“特征对象”只能在指针后面工作。
这是您的代码的非常简化版本。您有三种不同的结构实现相同的特征(摘要)
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和Engine512State),因此此规则使用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!()
};
答案 1 :(得分:3)
对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!()
};
}