好奇地重复通用特征模式:溢出评估需求 [英] Curiously recurring generic trait pattern: overflow evaluating the requirement
问题描述
我正在尝试用一堆字段实现一个通用结构,其中每个字段类型都应该知道整个结构的确切类型.这是一种策略模式.
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屋!