仅当字段是某个枚举变量时才为结构定义方法吗? [英] Defining a method for a struct only when a field is a certain enum variant?

查看:55
本文介绍了仅当字段是某个枚举变量时才为结构定义方法吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下结构:

#[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,
    },
}

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

Entry is an entry in a GameCube ROM file system table which describes a file or directory. I defined various methods for Entry such as Entry::read_filename and Entry::write_to_disk. However, I have some methods that don't make sense to be available to both regular files and directories. For example, Entry::iter_contents iterates over all of a directory's child entries.

我希望能够为 entry_type 是特定变体的条目定义某些方法,例如 Entry :: iter_contents .

I want to be able to define certain methods such as Entry::iter_contents only for entries where entry_type is a certain variant.

我尝试将 EntryType 转换为特征,并制作了一个 DirectoryEntryInfo FileEntryInfo 结构,它们都实现了 EntryType

I tried turning EntryType into a trait and made a DirectoryEntryInfo and FileEntryInfo struct, which both implemented EntryType.

遗憾的是,这种方法存在一些问题.我在其他地方有一个 Vec< Entry> ,通过此更改,它将变成 Vec< Entry< EntryType>> .使用这样的特征,我无法将 Entry< EntryList> 转换为 Entry< DirectoryEntryInfo> .我还尝试使用 Any 做某事,因为这是我知道在Rust中向下转换的唯一方法,但是我只能转换 entry_type ,而不是整个条目本身.

Sadly, there were some problems with this approach. I have a Vec<Entry> elsewhere and with this change it would become Vec<Entry<EntryType>>. Using a trait like this, I have no way to downcast Entry<EntryList> to Entry<DirectoryEntryInfo>. I also tried doing something with Any, as that is the only way I am aware of to downcast in Rust, but I was only able to cast entry_type, not the entire Entry itself.

最终,我想得到类似以下的内容:

Ultimately, I'd like to end up with something similar to this:

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> {
    ...
}

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

This way, I could access all of the entries fields without knowing whether or not it's a directory or file, as well as be able to cast it to a type that would provide me with all of the Entry fields in addition to methods based on the type parameter like Entry::iter_contents.

在没有 RFC 1450 的情况下,是否有一种很好的方法来做到这一点?

Is there a good way to do this without something like RFC 1450?

我知道枚举变量不是它们自己的类型,不能用作类型参数.我只是在寻找一种替代方法,以有条件地为结构定义方法,并且仍然能够以某种方式在 Vec 之类的东西中存储该结构的任何变体.这篇文章与我正在尝试做的非常接近.但是,使用其中的示例,如果不知道 Bool True 还是,就无法存储 MyEnum< Bool> > False 在编译时.能够将类似 MyEnum< Box< Bool>> 的内容转换为 MyEnum< False> 的东西可以解决此问题,但是我不知道Rust中有类似的东西.

I'm aware that enum variants are not their own types and cannot be used as type parameters. I am just looking for an alternate way to conditionally define a method for a struct and still be able to have a way to store any variant of this struct in something like a Vec. This article is extremely close to what I am trying to do. However, using the example from it, there is no way to store a MyEnum<Bool> without knowing whether that Bool is True or False at compile time. Being able to downcast something like MyEnum<Box<Bool>> to MyEnum<False> would fix this, but I'm not aware of anything like that in Rust.

推荐答案

不幸的是,您不能这样做,因为(如问题注释中所述)

Unfortunately, you can't do quite that, because (as mentioned in the question comments) enum variants are not types and information about the variant isn't available to the type system.

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

One possible approach is to "hoist" the enum to the outer layer and have each variant contain a struct that wraps the shared data:

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),
}

然后,您可以按照以下几行轻松定义 as_file as_dir :

Then you can easily define as_file and as_dir along the following lines:

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,
        }
    }
}

这是不理想的,因为您之前在 Entry 上编写的任何代码现在都需要以适当的变体形式推迟到 EntryInfo .使事情变得更容易的一件事是编写一个帮助方法来找到包装的 EntryInfo :

It's not ideal, because any code you would have written on Entry before now needs to defer to EntryInfo in the appropriate variant. One thing that can make things easier is writing a helper method to find the wrapped EntryInfo:

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

然后,您可以在实现 Entry 的过程中使用 self.as_info()代替 self.info .

Then you can use self.as_info() instead of self.info in the implementation of Entry.

这篇关于仅当字段是某个枚举变量时才为结构定义方法吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆