Why must I explicitly macro_use
macros not directly used by my code, but only used by my codes dependencies?
There are 2 cases illustrated below:
- Macros only used by my dependencies
call
,do_parse
,map
,take
,error_if
- Other scope names only used by my dependencies
parse_der_defined
(a function),fold_parsers
(a function),DerObject
(a struct),DerObjectContent
(a struct)
Bonus Question
What's the best workflow to deal with this while coding? Just compiler error, add the name, rinse, repeat?
// 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);
}
};
}
Macros are hygenic, but they don't "bring in" things from the scope they are defined in.
If you define a macro in one crate, and use relative names rather than absolute ones (if the macro produces code using
der_read_element_name
rather than::der_parser::der_read_element_name
), then you are required to useuse
to bring those methods into scope.The solution to this is to always use absolute names when defining macros, or to 'use' them inside the macro scope. For instance, if you have a macro which opened a file, you do one of two things. Either import:
or use absolute paths directly:
A similar thing happens with macros using other macros! If you have two crates, say,
cratea
with:and
crateb
with:then when someone uses
crateb
withconversation!()
, it literally expands tosay_hello!(); println!("goodbye");
, and this will error ifsay_hello
doesn't exist in the target crate.The solution to this is to re-export all macros from
cratea
tocrateb
. You can do this with the following:This will mean anyone who depends using
#[macro_use]
oncrateb
will have access to all ofcratea
's macros too. So, when your macro incrateb
expands to reference a macro incratea
, it will work.On workflow, personal anecdote:
I've found
cargo check
with cargo-watch to be the best workflow I know of. I'll startcargo watch
in a terminal, and whenever a file is saved it will start a check and just report syntax errors.Once I feel pretty confident and there are no errors, I'll actually run
cargo run
and/orcargo test
depending on the project.