好奇地重复通用特征模式:溢出评估需求 [英] Curiously recurring generic trait pattern: overflow evaluating the requirement

查看:124
本文介绍了好奇地重复通用特征模式:溢出评估需求的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试用一堆字段实现一个通用结构,其中每个字段类型都应该知道整个结构的确切类型.这是一种策略模式.

I am trying to implement a generic structure with a bunch of fields, where each of the field types should know about the exact type of the whole structure. It's a sort of strategy pattern.

pub struct Example<S: Strategy<Example<S, D>>, D> {
    pub s: S,
    pub a: S::Associated,
    pub data: D,
}
pub trait Strategy<T> {
    type Associated;
    fn run(&self, &T);
}
pub trait HasData {
    type Data;
    fn data(&self) -> &Self::Data;
}

impl<S: Strategy<Self>, D> Example<S, D> {
//               ^^^^
// the complex code in this impl is the actual meat of the library:
    pub fn do_it(&self) {
        self.s.run(self); // using the Strategy trait
    }
}
impl<S: Strategy<Self>, D> HasData for Example<S, D> {
    type Data = D;
    fn data(&self) -> &D {
        &self.data
    }
}

我打算从上面的库"中实例化泛型:

I was planning to then instantiate the generics from the above "library":

pub struct ExampleStrat;
pub struct ExampleData;

impl<E: HasData<Data = ExampleData>> Strategy<E> for ExampleStrat {
    type Associated = ();
    fn run(&self, e: &E) {
        let _ = e.data();
        // uses ExampleData here
    }
}
let example = Example {
    s: ExampleStrat,
    a: (),
    data: ExampleData,
};
example.do_it();

在我的实际代码中,我有很多不同的策略"以及多个数据字段,因此Example类型具有令人印象深刻的泛型列表,如果图书馆用户不需要,我很高兴对它们进行明确说明(或至少不经常使用),而只能使用HasData特征(及其关联类型,而不是通用类型参数).

In my actual code I've got quite a few different "strategies" and also multiple data fields, so the Example type has an impressive list of generics, and I'm happy if the library user doesn't need to be explicit about them (or not often at least) and instead can just use the HasData trait (with its associated types, not generic type parameters).

如果struct Example<S, D>中没有任何类型的绑定,这实际上可以(令人惊讶地)很好,比我最初预期的要好得多(在复制struct 上的impl特征范围.与受约束的类型有关,在我的情况下,我实际上需要使他们能够将Associated类型用于a字段.

If there was no type bound in struct Example<S, D>, this would actually work (surprisingly) fine, much better than I has initially expected (after fighting with Self in the struct bounds). However it is recommended to duplicate the impl trait bounds on the struct when the struct is only supposed to be used with the constrained types, and in my case I actually need them to be able to use the Associated type for the a field.

现在编译器在抱怨

error[E0275]: overflow evaluating the requirement `main::ExampleStrat: Strategy<Example<main::ExampleStrat, main::ExampleData>>`
  --> src/main.rs:42:9
   |
42 |         a: (),
   |         ^^^^^
   |
   = note: required because of the requirements on the impl of `HasData` for `Example<main::ExampleStrat, main::ExampleData>`
   = note: required because of the requirements on the impl of `Strategy<Example<main::ExampleStrat, main::ExampleData>>` for `main::ExampleStrat`

我该如何解决??我是在尝试做一些不可能的事情,我做错了还是应该这样做,但我却成为了编译器错误?我的完整设计有缺陷吗?

How can I solve this? Am I trying to do something that is not possible, am I doing it wrong, or is it supposed to be possible but I am falling prey to a compiler bug? Is my complete design flawed?

推荐答案

首先,如果您避免在特征和特征的定义中使用特征边界,那么一切都会变得更加清晰.当事情变得复杂时,约束至少可以从同一方向解决.

First of all, everything becomes a lot clearer if you avoid putting trait bounds on definitions of structs and traits. When things get complicated, the constraints are at least solved from the same direction.

pub struct Example<S, D, A> {
    pub s: S,
    pub a: A,
    pub data: D,
}

pub trait Strategy<T> {
    type Associated;
    fn run(&self, &T);
}

pub trait HasData {
    type Data;
    fn data(&self) -> &Self::Data;
}

impl<S, D, A> Example<S, D, A>
where
    S: Strategy<Self, Associated = A>,
{
    pub fn do_it(&self) {
        self.s.run(self);
    }
}

impl<S, D, A> HasData for Example<S, D, A>
where
    S: Strategy<Self, Associated = A>,
{
    type Data = D;
    fn data(&self) -> &D {
        &self.data
    }
}

您为ExampleStrat实现的Strategy看起来像这样:

Your implementation of Strategy for ExampleStrat looks like this:

impl<E: HasData<Data = ExampleData>> Strategy<E> for ExampleStrat {
    type Associated = ();
     // ...
}

这意味着您正在为所有可能的限定类型E定义它.现在,类型检查器只能查看特征范围,这些特征范围又是通用的,并且仅以其他特征来表示,它们以彼此为界,因此类型检查器进入了一个循环.通过为其指定具体类型,将其放在循环中.

What this means is that you are defining it for all possible qualifying types E. The type-checker can now only look at the trait bounds, which are again generic and only expressed in terms of other traits, which use each other as bounds, so the type-checker gets into a cycle. Put a block in the cycle by giving it a concrete type, which you know.

pub struct ExampleStrat;
pub struct ExampleData;

impl Strategy<Example<ExampleStrat, ExampleData, ()>> for ExampleStrat {
    type Associated = ();
    fn run(&self, e: &Example<ExampleStrat, ExampleData, ()>) {
        let _ = e.data();
        // uses ExampleData here
    }
}

fn main() {
    let example = Example {
        s: ExampleStrat,
        a: (),
        data: ExampleData,
    };
    example.do_it();
}

这篇关于好奇地重复通用特征模式:溢出评估需求的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆