4个版本
0.2.2 | 2018年12月20日 |
---|---|
0.2.1 | 2018年12月19日 |
0.2.0 | 2018年12月19日 |
0.1.0 | 2018年9月14日 |
#287 在 过程宏 中
16KB
148 行
概览
此crate提供了一个伪实现,实现了许多命名参数RFC之一。由于宏的使用而不是真正的编译器支持,它与实际的事物并不完全相同。此crate提供了一个类似于属性的进程宏,用于注解函数以生成命名参数支持。
该注解执行以下操作:
- 生成一个与函数具有相同参数的struct
- 以及一个关联的builder()函数
- 默认情况下,此struct隐藏在文档中
- 为该struct方法生成builder
- 它是一个类型安全的builder,要求每个没有默认值的函数都必须调用
- build函数将builder转换回参数struct
- 此struct也默认隐藏在文档中
- 可以在注解中为每个字段指定默认值
- 字段类型被Option包装(例如,T从T变为Option)
- 在builder上指定None作为默认值
- 并且具有Option类型,字段setter的builder接受T或Option
- 还可以指定位置参数(不允许为这些参数指定默认值)
- 生成与函数同名的一个宏,该宏接受命名参数(仅在nightly版本中支持)
- 创建builder
- 设置传入的任何参数
- 调用build(如果没有提供所有命名值,则在没有默认值的情况下将是一个编译错误)
- 还有一个退出宏 ——
n!
,您可以将函数调用包裹在其中以启用命名/默认参数语法。
所以,我们如何在几个命名空间(fn、struct和宏)中重载方法名称,以便消费者可以使用纯fn或宏
限制
本实现放弃了声明方面的处理 - 例如如何声明参数为命名参数以及如何指定参数默认值。它而是在属性中指定所有这些,以避免解析问题。未来的版本可以从类似属性的 proc macro 切换到类似函数的 proc macro,以实际实验声明语法。
这个版本对泛型处理得不好。这似乎是这个方法的一个可解决的限制。
这个版本只适用于独立函数,不适用于 impl 块中的函数。这也应该可以解决。
这个版本需要 nightly(由于 decl. macro 2.0 和 proc_macro_gen)以及 2018 版本(由于宏路径)。虽然 proc_macro_gen 可能很快就会稳定,但 decl. macro 2.0 不会(我认为)。decl. Macros 2.0 是用来代替 macro_rules 的,以便为生成的宏提供正确的模块命名空间支持。
可能是由于错误造成的。
从 rubber_duck 导出的宏没有出现在 rubber_duck 文档中。
为 api declarer 生成的宏没有在生成的文档的正确模块中显示 GitHub 问题 #54112
使用
先决条件
- 使用 edition 2018(不同版本之间的宏路径与我所做的工作交互非常差)
Nightly 与 Not-Nighlty
这个 crate 有一些仅在 nightly 中可用的功能。
到目前为止,还没有夜间与非夜间功能的检测。相反,在声明您想使用命名/默认参数语法调用的函数的 crate 中,您必须通过使用 features=["nightly"]
来选择加入夜间仅有的功能。
编写 API
要设置一个 crate,以便发布可以使用命名/默认参数语法调用的方法,您需要对 Cargo.toml 和 lib.rs 进行一些初始设置工作。然后您将能够注释方法。任何消耗 crate 都不需要做任何不同的事情。
设置
如果您想使用夜间功能,请将此添加到您的 Cargo.toml 中
[dependencies]
rubber_duck = { version="0.2", features=["nightly"]}
否则,如果您在使用稳定版本,请添加此内容
[dependencies]
rubber_duck = "0.2"
然后在您的 lib.rs 的顶部添加此内容
// The following two lines (well, 4 if you count comments) are only needed when using features=["nightly"]
// Allows us to generate macros from macros
#![feature(proc_macro_hygiene)]
// Allows the use of declarative macros 2.0 (which are generated from the proc macro
#![feature(decl_macro)]
// These lines are needed on both nightly and stable
// Hide the base items the macro internals use
#[doc(hidden)]
// Import the base items the macro internals use
// This must be at the base of the crate
pub use rubber_duck::core::*;
注释函数
要使函数可以用命名语法调用,在函数所在的模块中导入宏
use ::rubber_duck::macros::*;
然后使用 #[gen_struct_sugar]
注释一个函数 - 这将足以生成一个可以以命名语法调用的宏。
此外,您可以声明 a) 位置参数和 b) 命名参数的默认值
#[gen_struct_sugar(
defaults( // You don't have to set defaults for all named parameters, but here we do
read = "false",
write = "false",
append = "false",
truncate = "false",
create = "false",
create_new = "false",
),
positionals(path), // Here we list the positional parameters in the order they appear
)]
pub fn open_file(
path: PathBuf,
read: bool,
write: bool,
append: bool,
truncate: bool,
create: bool,
create_new: bool,
) -> std::io::Result<File> {
OpenOptions::new()
.read(read)
.write(write)
.append(append)
.truncate(truncate)
.create(create)
.create_new(create_new)
.open(path)
}
消耗 API
调用方法时,使用感叹号!
- 任何位置参数都必须以正确的顺序先于命名参数,不使用名称,命名参数以name => value
的形式出现,其中name是文档中的发布参数名称
此外,消费者与写入器在不同的crate中,它们不需要启用任何功能标志
尽管如此,它们仍然需要在nightly上,可能是2018版(因为路径...)
完整示例
给定API声明
// Given this api declaration:
mod module {
#[gen_struct_sugar(
defaults(greeting = r#""Hello.""#),
positionals(name),
)]
pub fn is_a_test(name: &'static str, greeting: &'static str, message: &'static str) -> String {
format!("Dear {}, {}. {}", &name, &greeting, &message)
}
}
// One can call the function in a variety of ways
mod stable {
use crate::{n,module::is_a_test};
// Named form requires a macro
n!(is_a_test{"George", {greeting: "Hi.", message: "Rust is cool."}}); // Dear George, Hi. Rust is cool.
// and lets you use defaults
n!(is_a_test{"George", {message: "Hi."}}); // Dear George, Hello. Rust is cool.
// and even lets you use sugar (you need a trailing comma if there's only one named arg
let message = "Struct Sugar"; // Dear George, Hello. Struct Sugar
n!(is_a_test{"George", {message,}})
// Positional form doesn't need a macro, but args with defaults are wrapped in the option type
// Override the default
is_a_test("Bob", Some("Hi."), "Goodbye."); // Dear Bob, Hi. Goodbye.
// Use the default
is_a_test("Bob", None, "Goodbye."); // Dear Bob, Hello. Goodbye.
}
//There's also a slightly nicer way on nightly (behind the features=["nightly"] flag)
mod nightly_only {
use crate::module::is_a_test;
// Named form requires a macro
is_a_test!("George", greeting=> "Hi.", message=> "Rust is cool"); // Dear George, Hi. Rust is cool.
// and lets you use defaults
is_a_test!("George", message=> "Rust is cool"); // Dear George, Hello. Rust is cool.
}
// You don't even have to import it!
crate::module::is_a_test!("George", greeting=> "Hi.", message=> "Rust is cool");
此外,请查看github仓库中的example_api和example_consumer目录。
依赖项
~2MB
~45K SLoC