使用特征作为幻像类型
Using a trait as phantom type
在 Rust 中,我想使用幻像类型来正确键入一个简单的 id:
struct Id<T> {
val: u32,
_type: PhantomData<T>,
}
在初稿版本中,我使用了具体结构 T
,一切都很好。然后在使用不同数据源的更精细的版本中,这些结构变成了特征。比方说:
trait MyArticle {
fn get_id() -> Id<MyArticle>;
}
但是使用 traits 作为 phantom types 会带来问题:
- 编译器让我声明
T: ?Sized
,就好像可能需要 T
的大小一样。我可以接受,但是 PhantomData<T>
的目的是告诉 T
不会被使用,我想知道是否还有其他方法?
- 我收到警告:“不推荐使用没有显式 'dyn' 的特征对象”。我可以用 global
#![allow(bare_trait_objects)]
摆脱它,但这个警告在其他方面很有用,我不想那样做。有没有办法允许 bare_trait_object
只有 “当用作 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;
}
如果您不能或不想使特征对象安全,那么我认为您的空结构解决方案是可行的方法。请注意,如果您只需要一个空结构,您可以使用 struct MyArticle_;
.
在 Rust 中,我想使用幻像类型来正确键入一个简单的 id:
struct Id<T> {
val: u32,
_type: PhantomData<T>,
}
在初稿版本中,我使用了具体结构 T
,一切都很好。然后在使用不同数据源的更精细的版本中,这些结构变成了特征。比方说:
trait MyArticle {
fn get_id() -> Id<MyArticle>;
}
但是使用 traits 作为 phantom types 会带来问题:
- 编译器让我声明
T: ?Sized
,就好像可能需要T
的大小一样。我可以接受,但是PhantomData<T>
的目的是告诉T
不会被使用,我想知道是否还有其他方法? - 我收到警告:“不推荐使用没有显式 'dyn' 的特征对象”。我可以用 global
#![allow(bare_trait_objects)]
摆脱它,但这个警告在其他方面很有用,我不想那样做。有没有办法允许bare_trait_object
只有 “当用作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;
}
如果您不能或不想使特征对象安全,那么我认为您的空结构解决方案是可行的方法。请注意,如果您只需要一个空结构,您可以使用 struct MyArticle_;
.