我有一系列相互关联的trait,并且包含更多的类型关联,这些类型关联应该是枚举,我在每个需要它的trait中重复Id
类型关联,以避免像<<World as World>::Exit as Exit>::SpotId
这样的深度嵌套的完全限定名,但这会导致混乱,因为我现在实现的行为依赖于这些"顶级" trait:
trait Accessible {
type Context: Ctx;
fn can_access(&self, ctx: &Self::Context) -> bool;
}
trait Id: Copy + Clone + Debug + Eq + Hash + Ord + PartialOrd {}
trait Location: Accessible {
type LocId: Id;
type ExitId: Id;
}
trait Exit: Accessible {
type ExitId: Id;
type SpotId: Id;
type LocId: Id;
}
trait World {
type Context: Ctx;
type Location: Location;
type Exit: Exit;
type SpotId: Id;
fn get_neighbors(&self, spot_id: Self::SpotId) -> &[Self::SpotId];
}
trait Ctx: Clone + Eq {
type World: World<Context = Self, SpotId = Self::SpotId>;
type SpotId: Id;
}
fn expand<T>(world: &impl World<Context = T>, ctx: &T, start: <T as Ctx>::SpotId)
where T: Ctx,
{
for spot in world.get_neighbors(start) { }
}
(on the rust playground)
这会在world.get_neighbors
上产生错误:
| expected `World::SpotId`, found `Ctx::SpotId`
= note: expected associated type `<impl World<Context = T> as World>::SpotId`
found associated type `<T as context::Ctx>::SpotId`
并且在SpotId
的一些其它用途上反之亦然。
有没有一种干净的方法来维护相互关联,并为(例如)SpotId
指定一个简单的类型名,这样命名SpotId
的哪个trait版本就不重要了?
我尝试添加更多的注解,以明确它们必须被推断为相同的类型:
trait World {
type Context: Ctx<SpotId = Self::SpotId, World = Self>;
type Location: Location + Accessible<Context = Self::Context>;
type Exit: Exit<SpotId = Self::SpotId> + Accessible<Context = Self::Context>;
type SpotId: Id;
}
但得到相同的误差。
我看过https://users.rust-lang.org/t/type-alias-for-readability-with-associated-traits/64044/2,它建议使用类型别名来管理完全限定名,但我不确定如何开始。
- 更新**:可以通过类型参数指定一个类型满足两个要求:
fn expand<T, S>(world: &impl World<Context = T, SpotId = S>, ctx: &T, start: S)
where T: Ctx<SpotId = S>, S: Id,
{
for spot in world.get_neighbors(start) { }
}
但随着开发的继续,我的函数看起来更像是
fn explore<T, S, L, E>(
world: &impl World<
Context = T,
SpotId = S,
Exit = impl Exit<ExitId = E, SpotId = S> + Accessible<Context = T>,
Location = impl Location<LocId = L> + Accessible<Context = T>,
>,
ctx: &T,
)
where
T: Ctx<SpotId = S, LocationId = L, ExitId = E> + Debug,
S: Id,
L: Id,
E: Id,
{ ... }
并且每当我添加另一个id类型的使用时,每个调用者都必须被修改(为了简洁,我在这里省略了一些)。
2条答案
按热度按时间bogh5gae1#
您所要做的就是要求这些关联的类型在
fn expand
的定义中是相同的类型,这可以通过引入另一个泛型参数来实现:添加参数
S
并将Ctx
和World
约束为具有SpotId = S
告诉编译器它们必须是相同的类型,以便允许调用此函数。(Playground)
7dl7o3gd2#
下面是最后的工作:忽略
Accessible
中的循环依赖,我重新组织类型,使World
只有一个路径到每个Id类型,然后在trait函数中使用完全限定语法。然后,我更改函数中的泛型类型,使用实现place trait的类型,而不是Id
,因为这样更容易编写trait边界。包括一些奇怪的情况,比如我添加的这个结构体,它只需要Ctx
和SpotId
,但必须提供它们相关的边界。我不确定
World::Exit
的绑定是否与World::Location
的绑定相同,但它是必需的,但我现在将其保留。