Определение метода для структуры только тогда, когда поле является определенным вариантом перечисления?
У меня есть следующая структура:
#[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.