使用特征作为幻影类型



在Rust中,我想使用幻影类型来正确地键入一个简单的id:

struct Id<T> {
val: u32,
_type: PhantomData<T>,
}

在第一个草案版本中,我使用了具体的结构作为T,一切都很好。然后,在使用不同数据源的更精细版本中,这些结构变成了特征。比方说:

trait MyArticle {
fn get_id() -> Id<MyArticle>;
}

但使用特征作为幻影类型会带来问题:

  • 编译器让我声明T: ?Sized,就好像可能需要T的大小一样。我可以接受,但由于PhantomData<T>的目的是告诉T不会被使用,我想知道是否有其他方法
  • 我得到警告:;不赞成使用没有显式"dyn"的trait对象;。我可以用全局#![allow(bare_trait_objects)]来消除它,但这个警告在其他方面很有用,我不想这么做。是否有一种方法允许CCD_ 7";当用作CCD_ 8">

我目前的解决方案是在空结构和特征之间重复名称类型:

struct MyArticle_ {};
trait MyArticle {
fn get_id() -> Id<MyArticle_>;
}

这很尴尬,但我找不到更好的了。

样本的问题在于理解特征是什么。事实上,它不是类型(这就是编译器要求T: ?Sized的原因(,而是对类型的要求。因此,解决方案相当简单:;真实的";类型你用结构声明做对了,它可以是一个选项。但通常使用关联类型更方便:

trait MyArticle {
type T;
fn get_id() -> Id<Self::T>
where
Self::T: MyArticle;
}
// so given impls
struct X;
impl MyArticle for X {
type T = u32;
fn get_id() -> Id<u32> {
todo!()
}
}
impl MyArticle for u32 {
type T = u32;
fn get_id() -> Id<u32> {
todo!()
}
}

最后,您可以调用X::get_id(),或者完全限定的版本:<X as MyArticle>::get_id()

此外,你可能会在那里读到为什么fn get_id() -> Id<Box<dyn MyArticle>>不起作用。

这里的问题是特征本身不是类型,尽管dyn Trait是。因此,当您编写Id<MyArticle>时,它实际上意味着Id<dyn MyArticle>(因此发出警告(,如果MyArticle不是对象安全的,它就不会编译。

在这种特殊情况下,您可以使MyArticle对象安全:

use std::marker::PhantomData;
struct Id<T: ?Sized> {
val: u32,
_type: PhantomData<T>,
}
trait MyArticle {
fn get_id() -> Id<dyn MyArticle> where Self: Sized;
}

如果你不能或不想让trait对象安全,那么我认为你的空结构解决方案是可行的。请注意,如果您只需要一个空的结构,您可以使用只写struct MyArticle_;

最新更新