Можно ли определить, является ли поле определенного типа или реализует определенный метод в процедурном макросе? - PullRequest
1 голос
/ 01 апреля 2019

Я создал процедурный макрос, который реализует черту, но для того, чтобы это работало, мне нужно получить необработанные байты для каждого поля.Проблема в том, как получить байты поля отличается в зависимости от типа поля.

Есть ли способ проверить, существует ли функция в поле и не пытается ли она выполнить другую функцию?

Например, что-то вроде этого:

if item.field::function_exist {
    //do code
} else {
    //do other code
}

В настоящее время яЯ смотрю на создание другой функции trait / member, которую мне просто нужно создать для всех примитивов и создать процедурный макрос для больших полей, таких как структуры.Например:

if item.field::as_bytes().exists {
    (&self.#index).as_bytes()
} else {
    let bytes = (&self.#index).to_bytes();
    &bytes
}

Со строкой она имеет функцию-член as_bytes, а i32 - нет.Это означает, что мне нужен дополнительный код, когда поле члена структуры не является строкой.Возможно, мне понадобится match, а не if, но для примера будет достаточно if.

1 Ответ

2 голосов
/ 01 апреля 2019

Можно ли определить, является ли поле определенного типа или реализует определенный метод в процедурном макросе?

Нет, это не так.

Макросыработать с абстрактным синтаксическим деревом (AST) кода Rust.Это означает, что вы в основном просто получаете символы, которые набрал пользователь.

Если код пользователя имеет что-то вроде type Foo = Option<Result<i32, MyError>>, и вы обрабатываете некоторый код, который использует Foo, макрос не будет знать, что это "на самом деле Option.

Даже если бы он знал тип, знать, какие методы доступны, было бы еще сложнее.Будущие ящики могут создавать черты, которые добавляют методы к существующим типам.В тот момент, когда выполняется процедурный макрос, эти ящики, возможно, еще даже не были скомпилированы.

Я смотрю на создание другой функции trait / member, которую мне просто нужно создать для всех примитивов.и создайте процедурный макрос для больших полей, таких как структуры.

Это правильное решение.Если вы посмотрите на любой хорошо используемый процедурный макрос, это именно то, что он делает.Это позволяет компилятору делать то, для чего предназначен компилятор.

Это также намного лучше для удобства сопровождения - теперь эти примитивные реализации живут в стандартном файле Rust, а не встроены в макрос.Гораздо проще читать и отлаживать.

У вашего ящика будет что-то вроде этого:

// No real design put into this trait
trait ToBytes {
    fn encode(&self, buf: &mut Vec<u8>);
}

impl ToBytes for str {
    fn encode(&self, buf: &mut Vec<u8>) {
        buf.extend(self.as_bytes())
    }
}

// Other base implementations

И ваш процедурный макрос реализует это простым способом:

#[derive(ToBytes)]]
struct Foo {
    a: A,
    b: B,
}

становится

impl ToBytes for Foo {
    fn encode(&self, buf: &mut Vec<u8>) {
        ToBytes::encode(&self.a);
        ToBytes::encode(&self.b);
    }
}

В качестве конкретного примера, Serde делает то же самое с множественными способами сериализации в двоичные данные и из них:

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...