在HashMap中存储类型以动态实例化它们

时间:2018-03-31 12:52:18

标签: rust

我正在尝试将结构存储在由字符串键入的HashMap中,以便稍后我可以按字符串创建新对象。想象一下REST API,客户端可以通过提供名称让服务器实例化特定对象。

use std::collections::HashMap;

struct MyStruct;

impl MyStruct {
    pub fn new() -> Self {
        Self {}
    }
}

struct MyOtherStruct;

impl MyOtherStruct {
    pub fn new() -> Self {
        Self {}
    }
}

fn main() {
    let mut h = HashMap::new();
    h.insert("MyStruct", MyStruct);
    h.insert("MyOtherStruct", MyOtherStruct);

    // This is pseudo-code
    let obj = h.get("MyStruct").unwrap()::new();
}

正如我所料,由于语法错误,这不起作用:

error: expected one of `.`, `;`, `?`, or an operator, found `::`
  --> src/main.rs:25:41
   |
25 |     let obj = h.get("MyStruct").unwrap()::new();
   |                                         ^^ expected one of `.`, `;`, `?`, or an operator here

我的第二次尝试是存储对每个结构的new方法的引用,而不是类型本身。

use std::collections::HashMap;

struct MyStruct;

impl MyStruct {
    pub fn new() -> Self {
        Self {}
    }
}

struct MyOtherStruct;

impl MyOtherStruct {
    pub fn new() -> Self {
        Self {}
    }
}

fn main() {
    let mut h = HashMap::new();
    h.insert("MyStruct", &MyStruct::new);
    h.insert("MyOtherStruct", &MyOtherStruct::new);

    let obj = h.get("MyStruct").unwrap()();
}

此操作失败,因为fn项目的类型不同,并且无法存储在同一HashMap中:

error[E0308]: mismatched types
  --> src/main.rs:22:31
   |
22 |     h.insert("MyOtherStruct", &MyOtherStruct::new);
   |                               ^^^^^^^^^^^^^^^^^^^ expected fn item, found a different fn item
   |
   = note: expected type `&fn() -> MyStruct {MyStruct::new}`
              found type `&fn() -> MyOtherStruct {MyOtherStruct::new}`

因为我对Rust很陌生,所以我没有想法。我该如何解决这个问题?

2 个答案:

答案 0 :(得分:1)

这最终根本不可能。在Rust中,局部变量存储在堆栈中,这意味着它们必须具有固定大小,在编译时已知。您的构造需要在运行时确定堆栈上的值的大小。

最接近的选择是转移到特征对象,这会引入一个间接层:

use std::collections::HashMap;

trait NewThing {
    fn new(&self) -> Box<Thing>;
}
trait Thing {}

struct MyStruct;

impl NewThing for MyStruct {
    fn new(&self) -> Box<Thing> {
        Box::new(Self {})
    }
}
impl Thing for MyStruct {}

struct MyOtherStruct;

impl NewThing for MyOtherStruct {
    fn new(&self) -> Box<Thing> {
        Box::new(Self {})
    }
}
impl Thing for MyOtherStruct {}

fn main() {
    let mut h: HashMap<_, Box<NewThing>> = HashMap::new();
    h.insert("MyStruct", Box::new(MyStruct));
    h.insert("MyOtherStruct", Box::new(MyOtherStruct));

    let obj = h["MyStruct"].new();
}

你会在世界上找到这种模式,例如在超级NewService中。

  

调用&self

时,方法new的{​​{1}}的值是多少?

它是h["MyStruct"].new()MyStruct的一个实例。同一类型可以实现这两个特征的唯一原因是因为&#34;工厂&#34;没有真正的独特状态。和&#34;实例&#34;。在更复杂的实现中,这些将是两种不同的类型。

对于共享引用计数值等情况,使用相同类型是很常见的。

另见:

答案 1 :(得分:0)

以下是@ Shepmaster解决方案的一个更复杂的示例,它使用不同类型的工厂和对象本身:

use std::collections::HashMap;

trait NewThing {
    fn new(&self) -> Box<Thing>;
}
trait Thing {
    fn execute(&mut self);
}

// MyStruct
struct MyStructFactory;
impl NewThing for MyStructFactory {
    fn new(&self) -> Box<Thing> {
        Box::new(MyStruct {test: 12, name: "Test".into()})
    }
}

struct MyStruct {
    test: i32,
    name: String
}

impl Thing for MyStruct {
    fn execute(&mut self) { 
        self.test+=1;
        println!("MyStruct {} {}", self.test, self.name);
    }
}

// MyOtherStruct
struct MyOtherStructFactory;
impl NewThing for MyOtherStructFactory {
    fn new(&self) -> Box<Thing> {
        Box::new(MyOtherStruct {my_member: 1})
    }
}

struct MyOtherStruct {
    my_member: u32
}

impl Thing for MyOtherStruct {
    fn execute(&mut self) { println!("MyOtherStruct.my_member: {}", self.my_member); }
}

fn main() {
    let mut h: HashMap<_, Box<NewThing>> = HashMap::new();
    h.insert("MyStruct", Box::new(MyStructFactory));
    h.insert("MyOtherStruct", Box::new(MyOtherStructFactory));

    h["MyStruct"].new().execute();
    h["MyOtherStruct"].new().execute();
}