【问题标题】:Why must I use macros only used by my dependencies为什么我必须使用仅由我的依赖项使用的宏
【发布时间】:2018-06-16 08:34:52
【问题描述】:

为什么我必须显式地macro_use 宏不直接由我的代码使用,而仅由我的代码依赖项使用?

下图有两种情况:

  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);
        }
    };
}

【问题讨论】:

  • 宏在使用的地方展开。所以它使用的任何东西都必须在扩展站点上可用。
  • 虽然通常您不需要将宏使用的函数引入作用域,因为编写良好的宏在使用函数时会使用绝对名称。

标签: rust


【解决方案1】:

宏是卫生的,但它们不会“引入”它们定义范围内的东西。

如果您在一个 crate 中定义宏,并使用相对名称而不是绝对名称(如果宏使用 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");
    })
}

然后当有人将cratebconversation!() 一起使用时,它实际上会扩展为say_hello!(); println!("goodbye");,如果目标箱中不存在say_hello,则会出错。

解决方案是将所有宏从cratea 重新导出到crateb。您可以通过以下方式做到这一点:

extern crate cratea;
pub use cratea::*;

这意味着任何依赖于crateb 使用#[macro_use] 的人也可以访问cratea 的所有宏。因此,当 crateb 中的宏扩展为引用 cratea 中的宏时,它将起作用。


关于工作流程,个人轶事:

我发现cargo checkcargo-watch 是我所知道的最好的工作流程。我将在终端中启动cargo watch,每当保存文件时,它都会开始检查并报告语法错误。

一旦我感到非常自信并且没有错误,我会根据项目实际运行cargo run 和/或cargo test

【讨论】:

  • 当从宏定义 crate 中引用函数时,您需要在宏中绝对路径的开头使用$crate
  • 真的!我会尝试找到一种方法来解决这个问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-25
相关资源
最近更新 更多