我有以下结构:
#[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,
},
}
Entry
是GameCube ROM文件系统表中的一个条目,用于描述文件或目录。我为Entry
定义了各种方法,例如Entry::read_filename
和Entry::write_to_disk
。但是,我有一些方法对常规文件和目录都没有意义。例如,Entry::iter_contents
遍历所有目录的子条目。
我希望能够仅为Entry::iter_contents
是特定变体的条目定义某些方法,例如entry_type
。
我尝试将EntryType
转换为特征并制作了DirectoryEntryInfo
和FileEntryInfo
结构,两者都实现了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> {
...
}
这样,我可以访问所有条目字段,而无需知道它是否是目录或文件,并且能够将其转换为能够为我提供所有{{1除了基于类型参数的方法(如Entry
。
有没有像RFC 1450这样的方法可以做到这一点?
我知道枚举变体不是他们自己的类型,不能用作类型参数。我只是在寻找另一种方法来有条件地为结构定义一个方法,并且仍然能够有一种方法来存储这个结构的任何变体,如Entry::iter_contents
。 This article非常接近我要做的事情。但是,使用其中的示例,无法在编译时知道Vec
是MyEnum<Bool>
还是Bool
,而无法存储True
。能够将诸如False
之类的内容转发给MyEnum<Box<Bool>>
会解决这个问题,但我并不知道Rust中的任何内容。
答案 0 :(得分:3)
不幸的是,你不能相当,因为(如问题评论中所述)enum variants are not types并且类型系统无法获得有关变体的信息。
一种可能的方法是将enum
“提升”到外层,并让每个变体都包含一个包裹共享数据的struct
:
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
:
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
:
fn as_info(&self) -> &EntryInfo {
match *self {
Entry::Dir(ref d) => &d.info,
Entry::File(ref f) => &f.info,
}
}
然后,您可以在self.as_info()
的实施中使用self.info
代替Entry
。