Возможно ли, чтобы признак возвращал объекты признака, заданные универсальным типом в признаке? - PullRequest
1 голос
/ 13 мая 2019

Я хотел бы определить черту следующим образом:

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;
}

1 Ответ

1 голос
/ 14 мая 2019

Общая черта может быть параметризована для типа, но не для другой черты.Таким образом, в 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> { /* ... */ }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...