Почему я должен использовать макросы, используемые только моими зависимостями
Почему я должен явно macro_use
макросы, не используемые непосредственно моим кодом, но используемые только моими зависимостями кода?
Ниже приведены 2 случая:
- Макросы, используемые только моими зависимостями
call
,do_parse
,map
,take
,error_if
- Другие имена областей, используемые только моими зависимостями
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
в зависимости от проекта.