如何检查通用类型是否实现了通用特质?

3

我想在不创建对象的情况下检查一个类型是否实现了一个trait。但是它无法编译通过。请参见代码中的注释。那么我应该怎么做才能达到我的目标?

#![feature(specialization)]

struct T1;
struct T2;

trait A {}

impl A for T1 {}

trait Get_Static<TraitType> {
    fn has_trait() -> bool ;
}
default impl<TraitType, T> Get_Static<TraitType> for T
{
    fn has_trait() -> bool { false }
}
impl<TraitType, T> Get_Static<TraitType> for T where T:TraitType
{
    fn has_trait() -> bool { true }
}//Compiler complains TraitType is not a trait but type parameter

fn main() {
    if <T1 as Get_Static>::<A>::has_trait() {println!("{}", true)} else {println!("{}", false)}
    if <T2 as Get_Static>::<A>::has_trait() {println!("{}", true)} else {println!("{}", false)}
//This is surely wrong syntax but I don't know the right syntax
}

Unsize 可以检查一个类型是否实现了一个 trait;不幸的是,这是 我找到的最远的进展 - Matthieu M.
@MatthieuM。它仍然无法编译,我不知道<T1 as GetStatic<A>>是什么意思,你是在创建对象还是直接调用“非实例”方法?因为我真的不想要一个T对象... - 炸鱼薯条德里克
如果它编译通过,我就会发布答案。我提到“至少完成到了哪里”是有原因的 :) 我希望有人能够接手并开始推进。至于 <Type as Trait>,它是一种通用语法,用于获取TypeTrait 实现,然后允许您调用 Trait 的相关函数;在您的情况下,您正在请求 T1 实现的 GetStatic<A> trait,因此使用 <T1 as GetStatic<A>> - Matthieu M.
@MatthieuM。尝试:trait GetStatic<TraitType: ?Sized> {…} - Stefan
@Stefan:哎呀! - Matthieu M.
1个回答

1

警告:这个解决方案已经无法在最近的夜间版本(2022年1月)中使用,而且不知道它停止工作的时间。


感谢 Stefan 平滑了最后一个皱纹。
<T2 as Get_Static>::<A>::has_trait()
  //This is surely wrong syntax but I don't know the right syntax

这里尝试调用:
  • 一个与特定类型相关联的trait函数,
  • 针对该类型进行了实现。

语法为<Type as Trait>::associated_function()。在本例中,TypeT1TraitGet_Static<A>,因此应该是:

<T2 as Get_Static<A>>::has_trait()
impl<TraitType, T> Get_Static<TraitType> for T where T:TraitType
{
    fn has_trait() -> bool { true }
}
//Compiler complains TraitType is not a trait but type parameter

直接指定 TraitTypetrait 是不可能的,但是可以使用 Unsize 标记来检查是否满足 T: Unsize<TraitType>,这对我们的目的已经足够。

这需要进行以下三个更改:

  • 启用夜间特性 #![feature(unsize)],因为 Unsize 标记是不稳定的,
  • 允许 Get_Static 泛型参数为 ?Sized,因为 trait 是无大小的,
  • 在实现中使用 T: Unsize<TraitType> 作为约束条件。

总之,这意味着:

#![feature(specialization)]
#![feature(unsize)]

trait GetStatic<TraitType: ?Sized> {
    fn has_trait() -> bool ;
}

default impl<TraitType: ?Sized, T> GetStatic<TraitType> for T {
    fn has_trait() -> bool { false }
}

impl<TraitType: ?Sized, T> GetStatic<TraitType> for T 
    where
        T: std::marker::Unsize<TraitType>
{
    fn has_trait() -> bool { true }
}

然后被用作:
struct T1;
struct T2;

trait A {}

impl A for T1 {}

fn main() {
    println!("{}", <T1 as GetStatic<A>>::has_trait());
    println!("{}", <T2 as GetStatic<A>>::has_trait());
}

在游乐场上看它如何运作


谢谢,我扩展了你的代码,但又失败了,你有什么想法吗?https://gist.github.com/Xtricman/bf184572e557472c5f67e645c0a7d973 - 炸鱼薯条德里克
@神秘德里克:我怀疑目前无法进行转换。正如我所提到的,Rust泛型没有种类,因此TraitType永远不会作为一个trait。Unsize技巧是一种绕路的方式,也不是完全可靠的,因为您可以使用任何其他非大小类型而不是trait。我设法在没有'a的情况下使其工作这里,但我不确定那有多有用。 - Matthieu M.
1
这个不再起作用了。 - codyoss
1
@codyoss:确实;不确定这是否是rustc的一个bug,还是专业化规则发生了变化...这就是使用nightly的问题。 - Matthieu M.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接