仅当字段是某个枚举变体时才为结构定义方法?

Add*_*son 5 enums downcast type-safety type-parameter rust

我有以下结构:

#[derive(Debug)]
pub struct Entry {
    pub index: usize,
    pub name: String,
    pub filename_offset: u64,
    pub entry_type: EntryType,
}

#[derive(Debug)]
pub enum EntryType {
    File {
        file_offset: u64,
        length: usize,
    },

    Directory {
        parent_index: usize,
        next_index: usize,
    },
}
Run Code Online (Sandbox Code Playgroud)

Entry是GameCube ROM文件系统表中的一个条目,它描述了一个文件或目录.我定义了各种方法,EntryEntry::read_filenameEntry::write_to_disk.但是,我有一些方法对常规文件和目录都没有意义.例如,Entry::iter_contents遍历目录的所有子条目.

我希望能够定义某些方法,例如Entry::iter_contents仅针对entry_type某个变体的条目.

我尝试EntryType变成一个特征并制作了一个DirectoryEntryInfoFileEntryInfo结构,两者都实现了EntryType.

可悲的是,这种方法存在一些问题.我有一个Vec<Entry>其他地方,这种变化将成为Vec<Entry<EntryType>>.使用这样的特质,我也没有办法垂头丧气Entry<EntryList>Entry<DirectoryEntryInfo>.我也尝试过做一些事情Any,因为这是我所知道的唯一一种在Rust中贬低的方式,但我只能投entry_type,而不是整个Entry本身.

最终,我想最终得到类似的东西:

impl<T: EntryType> Entry<T> {
    pub fn as_dir(&self) -> Option<Entry<DirectoryEntryInfo>> { ... }
    pub fn as_file(&self) -> Option<Entry<FileEntryInfo>> { ... }
    ...
}

impl Entry<DirectoryEntryInfo> {
    ...
}

impl Entry<FileEntryInfo> {
    ...
}
Run Code Online (Sandbox Code Playgroud)

这样,我可以访问所有条目字段,而无需知道它是否是目录或文件,并且能够将其转换为Entry除了基于类型的方法之外还为我提供所有字段的类型参数如Entry::iter_contents.

有没有像RFC 1450这样的方法有一个很好的方法吗?

我知道枚举变体不是他们自己的类型,不能用作类型参数.我只是在寻找另一种方法来有条件地为结构定义一个方法,并且仍然能够以类似的方式存储这个结构的任何变体Vec.这篇文章非常接近我想要做的.但是,使用它的示例,无法在MyEnum<Bool>不知道Bool是在编译时True还是False在编译时存储.能够垂头丧气像MyEnum<Box<Bool>>MyEnum<False>可以解决这个问题,但我不知道这样的鲁斯特什么.

tre*_*tcl 5

不幸的是,您不能完全做到这一点,因为(如问题注释中所述)枚举变体不是类型,并且有关变体的信息不可用于类型系统。

一种可能的方法是将“提升”enum到外层,并让每个变体都包含struct包装共享数据的 a:

struct EntryInfo {
    index: usize,
    name: String,
    filename_offset: u64,
}

pub struct FileEntry {
    info: EntryInfo,
    file_offset: u64,
    length: usize,
}

pub struct DirEntry {
    info: EntryInfo,
    parent_index: usize,
    next_index: usize,
}

pub enum Entry {
    File(FileEntry),
    Dir(DirEntry),
}
Run Code Online (Sandbox Code Playgroud)

然后您可以按照以下方式轻松定义as_file和:as_dir

impl Entry {
    pub fn as_dir(&self) -> Option<&DirEntry> {
        match *self {
            Entry::Dir(ref d) => Some(d),
            _ => None,
        }
    }

    pub fn as_file(&self) -> Option<&FileEntry> {
        match *self {
            Entry::File(ref f) => Some(f),
            _ => None,
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这并不理想,因为您之前编写的任何代码Entry现在都需要遵循EntryInfo适当的变体。可以让事情变得更容易的一件事是编写一个辅助方法来查找包装的EntryInfo

fn as_info(&self) -> &EntryInfo {
    match *self {
        Entry::Dir(ref d) => &d.info,
        Entry::File(ref f) => &f.info,
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你可以在 的实现中使用self.as_info()而不是.self.infoEntry