Определение метода для структуры только тогда, когда поле является определенным вариантом перечисления?
У меня есть следующая структура:
#[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
, поскольку это единственный способ, который я знаю, чтобы опуститься в ржавчине, но я смог только бросить 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> {
...
}
Таким образом, я мог бы получить доступ ко всем полям записей, не зная, является ли это каталог или файл, а также иметь возможность привести его к типу, который предоставил бы мне все поля Entry
в дополнение к методам, основанным на параметре типа Entry::iter_contents
.
Я знаю, что варианты перечисления не являются их собственными типами и не могут использоваться в качестве параметров типа. Я просто ищу альтернативный способ условно определить метод для структуры и все же иметь возможность хранить любой вариант этой структуры в чем-то вроде Vec
. Эта статья чрезвычайно близка к тому, что я пытаюсь сделать. Однако, используя пример из него, невозможно сохранить MyEnum<Bool>
, не зная, является ли это Bool
True
или False
во время компиляции. Будучи в состоянии подавить что-то вроде MyEnum<Box<Bool>>
to MyEnum<False>
исправил бы это, но я не знаю ничего подобного в Rust.
1 ответ:
К сожалению, вы не можете сделатьвполне , потому что (как упоминалось в комментариях к вопросу) варианты перечисления не являются типами и информация о варианте недоступна системе типов.
Один из возможных подходов состоит в том, чтобы" поднять "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
.