【问题标题】:How to use multiple levels of traits [duplicate]如何使用多级特征[重复]
【发布时间】:2021-01-20 17:42:24
【问题描述】:

我有多个级别的特征,不知道如何引用顶级对象中的类型。我收到有关未使用参数的消息,但如果我删除它们,我会收到有关它们丢失的消息!一个例子:

struct User {
  name: String,
}

trait UserStore {
  fn get_user(&self) -> User;
}

struct Tenant<U>
where
  U: UserStore,
{
  user_store: U,
}

trait TenantStore<U>
where
  U: UserStore
{
  fn get_tenant(&self) -> Tenant<U>;
}

// what to do here??
struct Application<T, U>
where
    T: TenantStore<U>,
    U: UserStore
{
  tenant_store: T,
}

错误:

parameter `U` is never used

unused parameter

help: consider removing `U`, referring to it in a field, or using a marker such as `std::marker::PhantomData`rustc(E0392)

如果我删除 U 我会得到:

cannot find type `U` in this scope

【问题讨论】:

  • 我打赌你想要一个关联类型而不是类型参数。见Struct with a generic trait which is also a generic trait
  • That answer applied here。虽然我建议从structs 中删除where 子句;它们似乎没有任何用途。
  • @trentcl 看起来关联类型可能是我所追求的。但是结构上的where 子句难道不是为了限制泛型参数的类型吗?例如,我不想拥有Tenant&lt;i32&gt;。还是我错过了什么?
  • 结构上的特征边界通常仅在 结构定义本身 以某种方式使用该特征时才有用。例如,std::iter::Peekable&lt;I: Iterator&gt; 包含一个I::Item,因此删除I: Iterator 绑定显然无法编译。当 trait 没有在结构本身中使用,而仅在定义其行为的 impl 块中使用时,仅将绑定添加到需要它的 impl 块中的冗余较少......
  • ... 就像在std::collections::HashMap&lt;K, V, S&gt; 中一样,它不会将KEq + HashSBuildHasher 绑定,但在定义使用这些特征的方法的impl 块中除外.毕竟,如果有人假设Tenant&lt;i32&gt; 有用,那么阻止他们真的很重要吗?在实现implied bounds 之前,在Tenant&lt;U&gt; 上添加边界(IMO)只是浪费打字。 impl 块的边界就足够了。

标签: rust traits


【解决方案1】:

原答案

您可以使用PhantomData 解决问题。然后解决方案看起来像 (playground)

struct User {
  name: String,
}

trait UserStore {
  fn get_user(&self) -> User;
}

struct Tenant<U>
where
  U: UserStore,
{
  user_store: U,
}

trait TenantStore<U>
where
  U: UserStore
{
  fn get_tenant(&self) -> Tenant<U>;
}

// what to do here??
struct Application<T, U>
where
    T: TenantStore<U>,
    U: UserStore
{
  tenant_store: T,
  phantom_u: std::marker::PhantomData<U>
}

更新#1

这似乎有点难看,因为我现在有一个额外的参数,每次创建应用程序时都需要填写,但永远不会使用。

您可以使用Associated types 解决这个问题。然后解决方案看起来像(playground):

struct User {
  name: String,
}

trait UserStore {
  fn get_user(&self) -> User;
}

struct Tenant<U>
where
  U: UserStore,
{
  user_store: U,
}

trait TenantStore
{
  type U: UserStore;
  fn get_tenant(&self) -> Tenant<Self::U>;
}

struct Application<T>
where
    T: TenantStore
{
  tenant_store: T,
}

【讨论】:

  • 如果有评论解释为什么我的帖子有-1 会很有帮助?它不正确吗?对于我在搜索重复项时错过的同一问题,我们是否已经有了问题/答案?
  • 我看到这是建议之一,但这是实现这一目标的唯一方法吗?这似乎有点难看,因为我现在有一个额外的参数,每次创建应用程序时都需要填写,但永远不会使用。
  • 这是我知道的唯一方法。它可能看起来不完美,但确实有效。
  • 您能否证明您为什么使用PhantomData&lt;U&gt; 而不是PhantomData&lt;fn() -&gt; U&gt;PhantomData&lt;fn(U) -&gt; U&gt;PhantomData&lt;fn(U)&gt;PhantomData&lt;*mut U&gt;PhantomData&lt;*const U&gt;?因为如果您只是使用PhantomData 来消除错误消息,那么您很可能选错了。
  • 嗨@trentcl,感谢您的提问。我不确定我理解你的问题是否正确。我使用PhantomData 向编译器展示了单态结构的最终类型将取决于U。顺便说一句,我不是该领域的专家。如果您能提供解释,我相信这将非常有帮助:何时可以使用每种方法。对于问题本身,这可能是一个绝妙的答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-09-11
  • 2014-09-12
  • 2018-05-03
  • 1970-01-01
  • 1970-01-01
  • 2018-09-27
  • 2019-12-27
相关资源
最近更新 更多