既然proc_macros 已经稳定了,怎么造出这样的东西?
据我所知,可以选择在 a 上添加#[proc_macro_attribute]注释fn whatsitsname(attrs: TokenStream, code: TokenStream) -> TokenStream,但我如何注册它?如何添加自定义属性?
是否可以构建一个不输出任何东西,而是存储状态以建立列表的宏,然后再构建一个实际使用该数据的宏?
例如:
trait SomeTrait {}
#[derive(mark)]
struct Person {}
impl SomeTrait for Person {}
#[derive(mark)]
struct Item {}
impl SomeTrait for Item {}
#[derive(mark)]
struct Object {}
impl SomeTrait for Object {}
create_mapper! // this then outputs the below function
//assuming for the fact that data is loaded correctly before this macro is used
fn select_item(kind: String) -> impl SomeTrait {
match kind {
"person" => Person,
"item" => Item,
"object" => Object,
}
}
Run Code Online (Sandbox Code Playgroud) 过程宏存在于它们自己的 crate 中,这些 crate 是为开发机器编译的(以便在编译使用它们的 crate 时可以执行它们)。过程宏包中的任何条件编译指令将相应地基于其编译环境,而不是调用包的编译环境。
\n\n当然,此类宏可以扩展为包含条件编译指令的标记,然后将在调用包的编译\xe2\x80\x94的上下文中对这些指令进行评估,但是,这并不总是可能或理想的。
\n\n如果希望扩展令牌本身成为调用包的编译环境的某个函数,则需要宏在其运行时确定该环境(当然是调用包的编译) -时间)。显然是该模块的完美用例std::env。
然而,rustc 不设置任何环境变量;而货物套装数量有限。特别是,一些关键信息(如目标架构/操作系统等)根本不存在。
\n\n我很欣赏调用包中的构建脚本可以为宏设置环境变量然后读取,但这给调用包作者带来了令人不满意的负担。
\n\n有没有什么方法可以让 proc 宏作者获得有关调用包的编译环境的运行时信息(我最感兴趣的目标体系结构和操作系统)?
\nmacros compilation environment-variables rust rust-proc-macros
我正在创建一个程序宏,它从某些配置文件自动生成一个库(它是一个寄存器布局,但这对于问题来说并不重要)。
我希望该库自动生成自动库随附的文档,并包含应与cargo test. 现在,我已经实现了其中大部分内容,但有一个问题我找不到解决方案。
假设我们有一个名为的库my_lib,我们在其中调用宏来填充它:
use my_macro_lib::hello;
hello!();
Run Code Online (Sandbox Code Playgroud)
它扩展到类似:
/// `foo` will always return `true`
/// ```
/// use my_lib;
/// assert!(my_lib::foo());
/// ```
pub fn foo() -> bool {
true
}
Run Code Online (Sandbox Code Playgroud)
这将按预期运行 -cargo doc将做正确的事情并将cargo test按预期运行文档测试。
问题是,在这个例子中,use my_lib被硬编码到这my_macro_lib显然是不可取的。
如何创建一个宏来推断正在进行调用的板条箱的名称?
我尝试使用macro_rules!程序宏内部来扩展$crate,但这违反了卫生规则。
我正在编写一个程序宏,我需要多次发出一个很长的标识符(可能是因为卫生,例如)。我quote!用来创建TokenStreams,但我不想一遍又一遍地重复长标识符!
例如,我想生成此代码:
let very_long_ident_is_very_long_indeed = 3;
println!("{}", very_long_ident_is_very_long_indeed);
println!("twice: {}", very_long_ident_is_very_long_indeed + very_long_ident_is_very_long_indeed);
Run Code Online (Sandbox Code Playgroud)
我知道我可以创建一个Ident并将其插入到quote!:
let my_ident = Ident::new("very_long_ident_is_very_long_indeed", Span::call_site());
quote! {
let #my_ident = 3;
println!("{}", #my_ident);
println!("twice: {}", #my_ident + #my_ident);
}
Run Code Online (Sandbox Code Playgroud)
到目前为止一切顺利,但我需要在我的代码库中的许多函数中使用该标识符。我希望它是const我可以在任何地方使用的。但是,这失败了:
const FOO: Ident = Ident::new("very_long_ident_is_very_long_indeed", Span::call_site());
Run Code Online (Sandbox Code Playgroud)
出现此错误:
let very_long_ident_is_very_long_indeed = 3;
println!("{}", very_long_ident_is_very_long_indeed);
println!("twice: {}", very_long_ident_is_very_long_indeed + very_long_ident_is_very_long_indeed);
Run Code Online (Sandbox Code Playgroud)
我怀疑这些功能是否会const很快被标记。
我可以使字符串本身成为一个常量:
const IDENT: &str = "very_long_ident_is_very_long_indeed";
Run Code Online (Sandbox Code Playgroud)
但是无论我想在哪里使用标识符,我都需要调用Ident::new(IDENT, Span::call_site()),这会很烦人。我只想写#IDENT在我的quote! …
我正在尝试过滤掉结构体中类型为 的所有字段bool。但syn::Type枚举似乎没有案例,或者我错误地阅读了定义:
pub enum Type {
Array(TypeArray),
BareFn(TypeBareFn),
Group(TypeGroup),
ImplTrait(TypeImplTrait),
Infer(TypeInfer),
Macro(TypeMacro),
Never(TypeNever),
Paren(TypeParen),
Path(TypePath),
Ptr(TypePtr),
Reference(TypeReference),
Slice(TypeSlice),
TraitObject(TypeTraitObject),
Tuple(TypeTuple),
Verbatim(TokenStream),
// some variants omitted
}
Run Code Online (Sandbox Code Playgroud)
我查看了源syn::Types代码,以检查省略了哪些变体,但这并没有给我带来任何进一步的帮助。这是我到目前为止所拥有的:
#[proc_macro_derive(Creator)]
pub fn derive_creator(_item: TokenStream) -> TokenStream {
let item = parse_macro_input!(_item as syn::DeriveInput);
let item_ident = item.ident;
let fields = if let syn::Data::Struct(syn::DataStruct {
fields: syn::Fields::Named(syn::FieldsNamed { ref named, .. }),
..
}) = item.data
{
named
} else {
panic!("You can derive Creator only on …Run Code Online (Sandbox Code Playgroud) 我正在尝试使用darling解析属性,并且我想支持以下用法:
// att not specified
#[derive(MyTrait)]
struct Foo(u64);
// att specified without an argument
#[derive(MyTrait)]
#[myderive(att)]
struct Foo(u64);
// att specified with an argument
#[derive(MyTrait)]
#[myderive(att(value = "String"))]
struct Foo(u64);
Run Code Online (Sandbox Code Playgroud)
这些是我的类型:
#[derive(FromDeriveInput)]
#[darling(attributes(myderive))]
struct MyDeriveInput {
#[darling(default)]
att: Option<MyAttr>,
}
#[derive(FromMeta, Default)]
struct MyAttr {
#[darling(default)]
value: Option<Path>,
}
Run Code Online (Sandbox Code Playgroud)
还有一个测试:
#[test]
fn test() {
let derive_input = syn::parse_str(
r#"
#[derive(MyTrait)]
#[myderive(att)]
struct Foo(u64);
"#,
)
.unwrap();
let parsed: MyDeriveInput = FromDeriveInput::from_derive_input(&derive_input).unwrap();
assert!(parsed.att.is_some());
}
Run Code Online (Sandbox Code Playgroud)
我收到此错误:
// att not specified
#[derive(MyTrait)] …Run Code Online (Sandbox Code Playgroud) 我正在尝试编写一个过程宏,它生成将所有字段加倍的方法f64。我让它在 ./src/main.rs 的单个字段中工作
use attr_macro::DoubleF64;
#[derive(DoubleF64)]
struct MyStruct {
my_string: String,
my_number: f64,
my_other_number: f64,
}
fn main() {
let mystruct = MyStruct {
my_string: "some str".to_string(),
my_number: 2.0,
my_other_number: 2.0,
};
println!("my_number * 2: {}", mystruct.double_my_number());
}
Run Code Online (Sandbox Code Playgroud)
和./proc_macro/src/lib.rs:
extern crate proc_macro;
use proc_macro::TokenStream;
use quote::{format_ident, quote};
use syn::{parse_macro_input, DeriveInput, FieldsNamed};
#[proc_macro_derive(DoubleF64)]
pub fn double_f64(input: TokenStream) -> TokenStream {
let DeriveInput { ident, data, .. } = parse_macro_input!(input);
let (func_name, fident) = if let syn::Data::Struct(s) = data { …Run Code Online (Sandbox Code Playgroud) 我正在编写一个库,其中包含具有自定义属性的自定义派生宏。为此,我使用darling. 因此,我的项目结构如下:
\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 pg-worm\n\xe2\x94\x82 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 pg-worm-derive\n\xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 src/lib.rs\n\xe2\x94\x82 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 src/lib.rs\nRun Code Online (Sandbox Code Playgroud)\n我的 proc 宏是这样指定的 ( pg-worm/pg-worm-derive/src/lib.rs):
use darling::FromDeriveInput;\nuse proc_macro::{self, TokenStream};\nuse syn::parse_macro_input;\n\n#[derive(FromDeriveInput)]\n#[darling(attributes(table))]\nModelInput {\n table_name: Option<String>\n}\n\n#[proc_macro_derive(Model)]\npub fn derive(input: TokenStream) -> TokenStream {\n let opts = ModelInput::from_derive_input(&parse_macro_input!(input)).unwrap();\n\n // macro magic happens\n}\nRun Code Online (Sandbox Code Playgroud)\n然后我从主文件 ( pg-worm/src/lib.rs) 中重新导出宏:
pub use pg_worm_derive::*;\n\npub trait Model {\n // trait specs\n}\nRun Code Online (Sandbox Code Playgroud)\n但是当我使用以下代码测试我的宏时(也在pg-worm/src/lib.rs):
#[cfg(test)]\nmod tests {\n use pg_worm::Model;\n\n #[derive(Model)]\n #[table(table_name = "person")]\n struct Person {\n id: i64,\n name: …Run Code Online (Sandbox Code Playgroud) 注意:这个问题并不是专门关于serde:它是关于过程宏和 const 函数。
我正在研究serde的派生宏为Serialize和生成的代码Deserialize,但我还没有弄清楚为什么它的所有代码都在const _: ().
例如,
#[derive(Deserialize)]
struct MyNewType(i32);
Run Code Online (Sandbox Code Playgroud)
扩展为(使用cargo expand):
#[doc(hidden)]
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
const _: () = {
#[allow(unused_extern_crates, clippy::useless_attribute)]
extern crate serde as _serde;
#[automatically_derived]
impl<'de> _serde::Deserialize<'de> for MyNewType {
fn deserialize<__D>(
__deserializer: __D,
) -> _serde::__private::Result<Self, __D::Error>
where
__D: _serde::Deserializer<'de>,
{
// generated implementation omitted for brevity
}
}
};
Run Code Online (Sandbox Code Playgroud)
我发现,如果我仔细复制块内的内容const _: () = {...},并在删除标签后将其粘贴回源代码中#[derive(Deserialize)],那么一切似乎都会编译并且反序列化继续正常工作。const _: () …
rust ×10
rust-proc-macros ×10
rust-macros ×3
macros ×2
compilation ×1
compile-time ×1
doctest ×1