我想定义一个这样的特征:
pub trait Provider<T> {
fn load(&self, name: &str) -> Box<dyn T>;
}
但这是不可能的:
error[E0404]: expected trait, found type parameter `T`
--> src/lib.rs:2:47
|
2 | fn load(&self, name: &str) -> Box<dyn T>;
| ^ not a trait
可能的解决方案是删除dyn
关键字,但是我希望Provider
的实现者返回特征对象,如下所示:
pub struct MaterialProvider {}
trait Material {}
impl Provider<Material> for MaterialProvider {
fn load(&self, name: &str) -> Box<dyn Material> { /*...*/ }
}
可以表达这样的概念吗?
我想用它来创建一个单一的“资源库”,该库可用于创建各种类型的对象。在编译时可能不知道应支持的确切类型,并且它们不一定需要实现相同的接口。
在C ++中,解决上述问题的一种方法如下:
#include <iostream>
#include <map>
class A {
public:
static std::string identifier() {
return "A";
}
};
class B {
public:
static std::string identifier() {
return "B";
}
};
class ProviderBase {};
template <typename T>
class Provider : public ProviderBase {
public:
virtual T* load() = 0;
};
class ProviderA : public Provider<A> {
public:
A* load() {
return new A;
}
};
class Manager {
std::map<std::string, ProviderBase*> providers;
public:
template<typename T>
void register_provider(Provider<T>* provider) {
providers[T::identifier()] = provider;
}
template<typename T>
T* load() {
auto p = providers.find(T::identifier());
if (providers.end() != p) {
return static_cast<Provider<T>*>(p->second)->load();
}
return nullptr;
}
};
int main(int argc, char* argv[]) {
Manager m;
ProviderA provider_a;
m.register_provider(&provider_a);
if (m.load<A>()) {
std::cout << "Loaded A" << std::endl;
} else {
std::cout << "Could not load A" << std::endl;
}
if (m.load<B>()) {
std::cout << "Loaded B" << std::endl;
} else {
std::cout << "Could not load B" << std::endl;
}
return 0;
}
答案 0 :(得分:1)
通用特征可以在类型上参数化,但不能在另一个特征上参数化。因此,在trait Provider<T>
中,T
是一种类型,而不是特征,这意味着没有dyn T
这样的东西。
但是,如果类型参数本身被允许作为特征对象类型,则这里需要的只是类型参数。为此,您只需要取消默认的Sized
边界,因为特征对象没有大小:
pub trait Provider<T: ?Sized> {
fn load(&self, name: &str) -> Box<T>;
}
pub struct MaterialProvider {}
trait Material {}
impl Provider<dyn Material> for MaterialProvider {
fn load(&self, name: &str) -> Box<dyn Material> { /* ... */ }
}