问题描述
在 Rust 中,我想使用幻像类型来正确键入一个简单的 id:
struct Id<T> {
val: u32,_type: PhantomData<T>,}
在初稿中,我使用了具体的结构作为 T
,一切都很好。然后在使用不同数据源的更精细版本中,这些结构成为特征。让我们说:
trait MyArticle {
fn get_id() -> Id<MyArticle>;
}
但是使用 trait 作为幻像类型会带来问题:
- 编译器让我声明
T: ?Sized
,好像T
的大小是潜在的需要。我可以接受,但由于PhantomData<T>
的目的是告诉T
不会被使用,我想知道是否还有其他方法? - 我收到警告:“不推荐使用没有明确 'dyn' 的 trait 对象”。我可以使用全局
#![allow(bare_trait_objects)]
来摆脱它,但此警告在其他方面很有用,我不想这样做。有没有办法允许bare_trait_object
only “当用作Id<T>
的类型参数时”?
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()
此外,您可能会阅读there 为什么 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_;
。