Почему я должен использовать макросы, используемые только моими зависимостями

Почему я должен явно macro_use макросы, не используемые непосредственно моим кодом, но используемые только моими зависимостями кода?

Ниже приведены 2 случая:

  1. Макросы, используемые только моими зависимостями
    • call, do_parse, map, take, error_if
  2. Другие имена областей, используемые только моими зависимостями
    • parse_der_defined (функция), fold_parsers (функция), DerObject (структура), DerObjectContent (структура)

Бонусный вопрос

Какой лучший рабочий процесс для решения этой проблемы во время кодирования? Просто ошибка компилятора, добавить имя, промыть, повторить?


// Why is this necessary? My code does not directly use macros from this scope.
#[macro_use(call, do_parse, map, take)]
extern crate nom;

// Why is this necessary? My code does not directly use macros from this scope.
#[macro_use(error_if)]
extern crate rusticata_macros;

// Why is this necessary? My code does not directly use macros from this scope.
#[macro_use(parse_der_sequence_defined, parse_der_defined, fold_parsers)]
extern crate der_parser;

// My code does not directly use these names. Why do I need to `use` them?
use der_parser::{der_read_element_header, DerObjectContent};

// Why is this necessary? My code does not directly use these names.
use nom::{Err, ErrorKind};

// I actually use these
use nom::IResult;
use der_parser::DerObject;

fn seq_of_integers(input: &[u8]) -> IResult<&[u8], DerObject> {
    parse_der_sequence_defined!(input, der_parser::parse_der_integer)
}

fn main() {
    let input = [
    0x30, // ASN.1 sequence
    0x03, // length 3 bytes
    0x02, // ASN.1 Integer
    0x01, // length 1 byte
    0x07, // 7
    ];
    let der_result = seq_of_integers(&input);
    match der_result {
        IResult::Done(_unparsed_suffix, der) => {
            assert_eq!(_unparsed_suffix.len(), 0);
            let der_objects = der.as_sequence().unwrap();
            for (index, der_object) in der_objects.iter().enumerate() {
                println!("{}: {}", index, der_object.content.as_u32().unwrap());
            }
        }
        IResult::Error(error) => {
            eprintln!("{}", error);
        }
        IResult::Incomplete(_needed) => {
            eprintln!("{:?}", _needed);
        }
    };
}

1 ответ

Решение

Макросы гигиеничны, но они не "вводят" вещи из области, в которой они определены.

Если вы определяете макрос в одном ящике и используете относительные имена, а не абсолютные (если макрос создает код, используя der_read_element_name скорее, чем ::der_parser::der_read_element_name), то вы обязаны использовать use ввести эти методы в сферу применения.

Решение этой проблемы заключается в том, чтобы всегда использовать абсолютные имена при определении макросов или "использовать" их внутри области макроса. Например, если у вас есть макрос, который открыл файл, вы делаете одну из двух вещей. Либо импорт:

macro_rules! open {
    ($file:expr) => ({
        // note: this has to be inside the macro expansion
        // `::std` means this works anywhere, not just in
        // the crate root where `std` is in scope.
        use ::std::fs::File;

        File::open($file)
    })
}

или используйте абсолютные пути напрямую:

macro_rules! open {
    ($file:expr) => ({
        ::std:fs::File::open($file)
    })
}

Подобное происходит с макросами, использующими другие макросы! Если у вас есть два ящика, скажем, cratea с:

macro_rules! say_hello {
    () => (println!("hi"))
}

а также crateb с:

#[macro_use]
extern crate cratea;

macro_rules! conversation {
    () => ({
        say_hello!();
        println!("goodbye");
    })
}

тогда, когда кто-то использует crateb с conversation!()буквально расширяется до say_hello!(); println!("goodbye");и это будет ошибка, если say_hello не существует в целевом ящике.

Решением этой проблемы является реэкспорт всех макросов из cratea в crateb, Вы можете сделать это с помощью следующего:

extern crate cratea;
pub use cratea::*;

Это будет означать любой, кто использует #[macro_use] на crateb будет иметь доступ ко всем crateaМакросы тоже. Итак, когда ваш макрос в crateb расширяется для ссылки на макрос в cratea, это будет работать.


О рабочем процессе, личный анекдот:

я обнаружил cargo check с грузовыми часами, чтобы быть лучшим рабочим процессом, о котором я знаю. Я начну cargo watch в терминале, и всякий раз, когда файл сохраняется, он запускает проверку и просто сообщает о синтаксических ошибках.

Как только я почувствую себя достаточно уверенно и ошибок не будет cargo run и / или cargo test в зависимости от проекта.

Другие вопросы по тегам