4

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

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

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

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

但是使用traits作为幻像类型会带来问题:

  • 编译器让我声明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_>;
}

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

4

2 回答 2

0

您的样本的问题在于了解特征是什么。实际上它不是类型(这就是编译器要求的原因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>>不起作用。

于 2021-06-11T16:12:41.303 回答
0

这里的问题是特征本身不是类型,尽管dyn Traits 是。所以当你写的时候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;
}

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

于 2021-06-11T23:57:53.570 回答