Я хотел бы определить черту следующим образом:
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;
}