仅当字段是某个枚举变量时才为结构定义方法吗? [英] Defining a method for a struct only when a field is a certain enum variant?
问题描述
我有以下结构:
#[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屋!