Есть ли способ, чтобы черта указала себя для связанного типа в другой черте, которую она расширяет? - PullRequest
2 голосов
/ 21 октября 2019

Я бы хотел определить черту, которая имеет в качестве супертрата другую черту с собственным типом объекта черты в качестве связанного типа:

/// A trait for making things.
trait Make {
    type Output: ?Sized;
    fn make(self: Box<Self>) -> Box<Self::Output>;
}

/// A special case of Make, which makes things that impl the same trait.
trait Bootstrapper: Make<Output = dyn Bootstrapper> {} // Will NOT compile.

Однако я не могу этого сделать, поскольку он создает бесконечное числоцикл. В приведенном выше примере мне нужно было бы указать Output для dyn Bootstrapper, что само по себе (dyn Bootstrapper). Но тогда мне нужно будет указать Output для , dyn Bootstrapper, и так далее, например, Make<Output = dyn Bootstrapper<Output = dyn Bootstrapper<Output = dyn Bootstrapper<...>>>.

Компилятор Rust, похоже, согласен с тем, чтоне будет работать:

error[E0391]: cycle detected when computing the supertraits of `Bootstrapper`
 --> src/lib.rs:8:1
  |
8 | trait Bootstrapper: Make<Output = dyn Bootstrapper> {} // Will NOT compile.
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: ...which again requires computing the supertraits of `Bootstrapper`, completing the cycle
note: cycle used when collecting item types in top-level module
 --> src/lib.rs:8:1
  |
8 | trait Bootstrapper: Make<Output = dyn Bootstrapper> {} // Will NOT compile.
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Я также не могу указать Output = Self, потому что это чрезмерно ограничит черту, так что данная реализация Bootstrapper может только на make() больше себя. Я хочу, чтобы Bootstrapper смог make() других видов Bootstrapper. Пожалуйста, посмотрите на эту Rust площадку для примера (примерно) того, что я пытаюсь сделать.

Есть ли способ обойти это, и Bootstrapper указать Bootstrapper( не Self) для Output?

1 Ответ

0 голосов
/ 27 октября 2019

Решение, которое я выбрал, заключалось в следующем:

/// A meta-`trait` that defines the associated types for a subset of `Make`.
trait Traits {
    type Output : ?Sized;
}

/// A `trait` for making things. Takes in a set of `Traits` that define the
/// output for the `make()` function.
trait Make {
    type Traits : Traits;
    fn make(self : Box<Self>) -> Box<<Self::Traits as Traits>::Output>;
}

Выделив связанные type s как отдельный мета-интерфейс Traits, я смог реализовать Traits дляновый конкретный тип и передайте его в Make:

/// `Traits` implementation for `Bootstrapper`, which constrains the `Make`
/// implementation for `Bootstrapper` to outputting a `Box<Bootstrapper>`.
struct BootstrapperTraits;
impl Traits for BootstrapperTraits {
    // NOTE: Specifying Self here gets around the circular dependency.
    type Output = dyn Bootstrapper<Traits = Self>;
}

/// A special case of Make that makes the same *kind* of thing as itself, but
/// not *necessarily* `Self`.
trait Bootstrapper : Make<Traits = BootstrapperTraits> {
    fn is_best(&self) -> bool;
}

Конкретный тип (BootstrapperTraits) может обойти зависимость круговых супертрейсов, указав Self в качестве type Traits для Output. Тогда BootstrapperTraits указывается как конкретная реализация Traits для Bootstrapper. Следовательно, любой, кто реализует Bootstrapper, должен теперь также реализовать Make<Traits = BootstrapperTraits>, что требует, чтобы Output было dyn Bootstrapper.

См. Эту Rust площадку для полного решения.

...