Идиоматический c способ возврата нескольких типов в Rust - PullRequest
0 голосов
/ 21 февраля 2020

Я пишу программу, которая анализирует несколько файлов. Эти файлы могут попадать в различные категории, и я не знаю, какие заранее. Имеет смысл создать тип для каждого типа данных, которые могут содержать файлы, но я изо всех сил пытаюсь получить этот тип данных из моего анализатора и в мою модель / основную программу.

В большинстве других языков программирования я вероятно, определит функцию / метод, который принимает имя файла в качестве входных данных и возвращает тип generi c в качестве выходных данных. В зависимости от моих потребностей, я затем приведу или протестирую тип моего родового c returntype и получу реальный тип моих перенастроенных данных.

Однако в Rust это, похоже, не работает. Я могу определить функцию, которая возвращает черту, например:

fn parse_file(file: &'str) -> impl ParsedData

, где ParsedData - общая черта для всех моих типов данных. Но что мне делать отсюда? В какой-то момент мне нужно разделить различные типы данных, скрытые за моей чертой ParsedData.

Решение 1: Я нашел черту Any, которая позволяет снизить черту до тип. Но, по общему мнению, этого следует избегать. Я не уверен, что точно понимаю , почему людям это не нравится, но если ничего другого, то, кажется, достаточно много работы / кода котельной плиты, чтобы начать работать.

Решение 2: Существует также возможность объединить все известные типы данных в один большой тип контейнера, например:

pub struct DataContainer {
    pub data_type_1: Option<DataType1>,
    pub data_type_2: Option<DataType2>,
    pub data_type_3: Option<DataType3>,
    pub data_type_4: Option<DataType4>,
}

fn parse_file(file: &'str) -> DataContainer 

, и просто передавать его, пока вам не понадобятся типы данных внутри него. .

Решение n: Похоже, существует ряд менее популярных решений


Чтение некоторых данных без знания типа заранее должно быть очень общая задача. Разве нет де-факто способа с этим справиться? Я несколько раз читал, что главная причина плохой поддержки в Rust заключается в том, что существует другой и лучший способ решения почти / каждого типа проблем, когда люди сталкиваются с необходимостью снижения данных.

Так вот мой вопрос. Как сделать вы справляетесь с этой проблемой простым идиоматическим c способом?

Определение проблемы:

  • Допустим, вы хотите прочитать несколько файлов , Некоторые из них будут содержать профили пользователей, некоторые будут содержать изображения (не привязанные к пользователю), а некоторые будут содержать рецепты приготовления.
  • Вы не знаете, что содержит файл, прежде чем его прочитать, но его легко разобрать, и там нет никакой неопределенности
  • Я определил функцию parse_file выше, но я думал, что это будет хорошим интерфейсом для чтения этих файлов, поскольку они, вероятно, используют много кода для открытия файла et c.

Ответы [ 2 ]

2 голосов
/ 21 февраля 2020

То, что вы хотите - это enum .

Если у вас есть типы DataType1, DataType2, DataType3 и DataType4, вы можете определить перечисление, которое гарантировано содержать только один из типов данных.

enum DataContainer {
    Type1(DataType1),
    Type2(DataType2),
    Type3(DataType3),
    Type4(DataType4),
}

Затем вы можете обработать каждый вариант с помощью match:

let data: DataContainer = parse(something);
match data {
    Type1(x) => do_something_with_type_1(x),
    Type2(x) => do_something_with_type_2(x),
    Type3(x) => do_something_with_type_3(x),
    Type4(x) => do_something_with_type_4(x),
}
0 голосов
/ 21 февраля 2020

Я думаю, что вы путаете черты с типами данных или обобщениями .

Черта сообщает компилятору Rust о функциональности определенный тип имеет и может делиться с другими типами

Таким образом, в основном fn parse_file(file: &'str) -> impl ParsedData возвращает тип, поддерживающий черту ParsedData, где он определен как:

trait ParsedData{
   fn some_funtion(&self) -> some_return_type;
}

Теперь вы можете сделать что-то подобное для всех типов data_types:

impl ParsedData for data_type_x{
    fn some_funtion(&self) -> some_return_type{
        // code
    }
}

Теперь вы можете напрямую использовать some_funtion для возвращаемого типа, где возвращается тип data_type (например: let k = parse_file(&"some_str").some_function();) .

Но если это тип возврата условный in:

fn parse_file(file: &'str) -> impl ParsedData{
   x
}

, измените его на:

fn parse_file(file: &'str) -> Box<ParsedData>{
   Box::new(x)
}
...