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

Tyr*_*ant 3 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();
}
Run Code Online (Sandbox Code Playgroud)

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

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();
}
Run Code Online (Sandbox Code Playgroud)

我的第二次尝试是存储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()();
}
Run Code Online (Sandbox Code Playgroud)

这失败了,因为这些fn项目具有不同的类型并且不能存储在相同的HashMap:

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
Run Code Online (Sandbox Code Playgroud)

由于我对 Rust 还很陌生,所以我没有想法。我怎么解决这个问题?

She*_*ter 5

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

最接近的选择是移动到trait objects,它引入了一个间接层:

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();
}
Run Code Online (Sandbox Code Playgroud)

你会在世界上发现这种模式,比如在 hyper 的NewService.

调用时[&self方法的值new]是什么h["MyStruct"].new()

它是MyStructor的一个实例MyOtherStruct。同一个类型可以实现这两个特征的唯一原因是“工厂”和“实例”没有真正独特的状态。在更复杂的实现中,这将是两种不同的类型。

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

也可以看看: