如何在引用中两次使用迭代器!宏观?
How do I use an iterator twice inside of the quote! macro?
我正在尝试从 proc macro workshop 中实现构建器模式 我正在创建一个 proc 宏来解析结构,提取其 name
、field_names
和 field_types
.它应该重现结构本身,并创建一个具有相同 field_names
但具有 可选 类型的构建器结构。
我的问题是 field_name
和 field_type
是迭代器,我必须使用两次才能从一个结构中创建两个结构。
这是我的源代码树
.
├── Cargo.lock
├── Cargo.toml
├── builder-derive
│ ├── Cargo.toml
│ └── src
│ └── lib.rs
└── src
└── main.rs
./cargo.toml
[package]
name = "proc-macro-question"
version = "0.1.0"
authors = ["ropottnik <ropottnik@example.com>"]
edition = "2018"
[dependencies]
builder-derive = { path = "./builder-derive" }
./main.rs
#[derive(Builder)]
struct SomeStruct {
some_field: i32,
}
fn main() {
println!("Hello, world!");
}
./builder-derive/cargo.toml
[package]
name = "builder-derive"
version = "0.1.0"
authors = ["ropottnik <ropottnik@example.com>"]
edition = "2018"
[lib]
proc-macro = true
[dev-dependencies]
trybuild = { version = "1.0", features = ["diff"] }
[dependencies]
syn = { version= "1.0", features = ["extra-traits"] }
quote = "1.0"
./builder-derive/src/lib.rs
#[proc_macro_derive(Builder)]
pub fn derive(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let name = &input.ident;
let builder_name = format_ident!("{}Builder", &name);
let fields = match &input.data {
Data::Struct(DataStruct {
fields: Fields::Named(fields),
..
}) => &fields.named,
_ => panic!("expected a struct with named fields"),
};
let field_name = fields.iter().map(|field| &field.ident);
let field_type = fields.iter().map(|field| &field.ty);
let expanded = quote! {
pub struct #name {
#(#field_name: #field_type,)*
}
pub struct #builder_name {
#(#field_name: Option<#field_type>,)*
}
};
expanded.into()
}
$ cargo run
输出
warning: unused import: `Ident`
--> builder-derive/src/lib.rs:1:18
|
1 | use proc_macro::{Ident, TokenStream};
| ^^^^^
|
= note: `#[warn(unused_imports)]` on by default
error[E0382]: use of moved value: `field_name`
--> builder-derive/src/lib.rs:22:20
|
19 | let field_name = fields.iter().map(|field| &field.ident);
| ---------- move occurs because `field_name` has type `Map<syn::punctuated::Iter<'_, syn::Field>, [closure@builder-derive/src/lib.rs:19:40: 19:60]>`, which does not implement the `Copy` trait
...
22 | let expanded = quote! {
| ____________________^
23 | | pub struct #name {
24 | | #(#field_name: #field_type,)*
25 | | }
... |
29 | | }
30 | | };
| | ^
| | |
| |_____`field_name` moved due to this method call
| value used here after move
|
note: this function consumes the receiver `self` by taking ownership of it, which moves `field_name`
--> /Users/simon/.cargo/registry/src/github.com-1ecc6299db9ec823/quote-1.0.8/src/runtime.rs:53:28
|
53 | fn quote_into_iter(self) -> (Self, HasIter) {
| ^^^^
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0382]: use of moved value: `field_type`
--> builder-derive/src/lib.rs:22:20
|
20 | let field_type = fields.iter().map(|field| &field.ty);
| ---------- move occurs because `field_type` has type `Map<syn::punctuated::Iter<'_, syn::Field>, [closure@builder-derive/src/lib.rs:20:40: 20:57]>`, which does not implement the `Copy` trait
21 |
22 | let expanded = quote! {
| ____________________^
23 | | pub struct #name {
24 | | #(#field_name: #field_type,)*
25 | | }
... |
29 | | }
30 | | };
| | ^
| | |
| |_____`field_type` moved due to this method call
| value used here after move
|
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
我想我可以为每次使用创建一个迭代器,但这对我来说似乎不可能愚蠢 ;-)
迭代器只能使用零次或一次,不能多次;这是标准的 Rust,不涉及 quote!
宏:
fn example() {
let nums = std::iter::empty::<i32>();
for _ in nums {}
for _ in nums {}
}
error[E0382]: use of moved value: `nums`
--> src/lib.rs:4:14
|
2 | let nums = std::iter::empty::<i32>();
| ---- move occurs because `nums` has type `std::iter::Empty<i32>`, which does not implement the `Copy` trait
3 | for _ in nums {}
| ----
| |
| `nums` moved due to this implicit call to `.into_iter()`
| help: consider borrowing to avoid moving into the for loop: `&nums`
4 | for _ in nums {}
| ^^^^ value used here after move
|
note: this function consumes the receiver `self` by taking ownership of it, which moves `nums`
即使您通过可变引用获取迭代器,迭代一次也会耗尽它,第二次使用时不会留下任何值。
您需要克隆迭代器:
use quote::quote; // 1.0.8
fn example() {
let nums = std::iter::empty::<i32>();
let nums2 = nums.clone();
quote! {
#(#nums)*
#(#nums2)*
};
}
您还可以将迭代器收集到一个 Vec
中并对其进行多次迭代:
use quote::quote; // 1.0.8
fn example() {
let nums = std::iter::empty();
let nums: Vec<i32> = nums.collect();
quote! {
#(#nums)*
#(#nums)*
};
}
我正在尝试从 proc macro workshop 中实现构建器模式 我正在创建一个 proc 宏来解析结构,提取其 name
、field_names
和 field_types
.它应该重现结构本身,并创建一个具有相同 field_names
但具有 可选 类型的构建器结构。
我的问题是 field_name
和 field_type
是迭代器,我必须使用两次才能从一个结构中创建两个结构。
这是我的源代码树
.
├── Cargo.lock
├── Cargo.toml
├── builder-derive
│ ├── Cargo.toml
│ └── src
│ └── lib.rs
└── src
└── main.rs
./cargo.toml
[package]
name = "proc-macro-question"
version = "0.1.0"
authors = ["ropottnik <ropottnik@example.com>"]
edition = "2018"
[dependencies]
builder-derive = { path = "./builder-derive" }
./main.rs
#[derive(Builder)]
struct SomeStruct {
some_field: i32,
}
fn main() {
println!("Hello, world!");
}
./builder-derive/cargo.toml
[package]
name = "builder-derive"
version = "0.1.0"
authors = ["ropottnik <ropottnik@example.com>"]
edition = "2018"
[lib]
proc-macro = true
[dev-dependencies]
trybuild = { version = "1.0", features = ["diff"] }
[dependencies]
syn = { version= "1.0", features = ["extra-traits"] }
quote = "1.0"
./builder-derive/src/lib.rs
#[proc_macro_derive(Builder)]
pub fn derive(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let name = &input.ident;
let builder_name = format_ident!("{}Builder", &name);
let fields = match &input.data {
Data::Struct(DataStruct {
fields: Fields::Named(fields),
..
}) => &fields.named,
_ => panic!("expected a struct with named fields"),
};
let field_name = fields.iter().map(|field| &field.ident);
let field_type = fields.iter().map(|field| &field.ty);
let expanded = quote! {
pub struct #name {
#(#field_name: #field_type,)*
}
pub struct #builder_name {
#(#field_name: Option<#field_type>,)*
}
};
expanded.into()
}
$ cargo run
输出
warning: unused import: `Ident`
--> builder-derive/src/lib.rs:1:18
|
1 | use proc_macro::{Ident, TokenStream};
| ^^^^^
|
= note: `#[warn(unused_imports)]` on by default
error[E0382]: use of moved value: `field_name`
--> builder-derive/src/lib.rs:22:20
|
19 | let field_name = fields.iter().map(|field| &field.ident);
| ---------- move occurs because `field_name` has type `Map<syn::punctuated::Iter<'_, syn::Field>, [closure@builder-derive/src/lib.rs:19:40: 19:60]>`, which does not implement the `Copy` trait
...
22 | let expanded = quote! {
| ____________________^
23 | | pub struct #name {
24 | | #(#field_name: #field_type,)*
25 | | }
... |
29 | | }
30 | | };
| | ^
| | |
| |_____`field_name` moved due to this method call
| value used here after move
|
note: this function consumes the receiver `self` by taking ownership of it, which moves `field_name`
--> /Users/simon/.cargo/registry/src/github.com-1ecc6299db9ec823/quote-1.0.8/src/runtime.rs:53:28
|
53 | fn quote_into_iter(self) -> (Self, HasIter) {
| ^^^^
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0382]: use of moved value: `field_type`
--> builder-derive/src/lib.rs:22:20
|
20 | let field_type = fields.iter().map(|field| &field.ty);
| ---------- move occurs because `field_type` has type `Map<syn::punctuated::Iter<'_, syn::Field>, [closure@builder-derive/src/lib.rs:20:40: 20:57]>`, which does not implement the `Copy` trait
21 |
22 | let expanded = quote! {
| ____________________^
23 | | pub struct #name {
24 | | #(#field_name: #field_type,)*
25 | | }
... |
29 | | }
30 | | };
| | ^
| | |
| |_____`field_type` moved due to this method call
| value used here after move
|
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
我想我可以为每次使用创建一个迭代器,但这对我来说似乎不可能愚蠢 ;-)
迭代器只能使用零次或一次,不能多次;这是标准的 Rust,不涉及 quote!
宏:
fn example() {
let nums = std::iter::empty::<i32>();
for _ in nums {}
for _ in nums {}
}
error[E0382]: use of moved value: `nums`
--> src/lib.rs:4:14
|
2 | let nums = std::iter::empty::<i32>();
| ---- move occurs because `nums` has type `std::iter::Empty<i32>`, which does not implement the `Copy` trait
3 | for _ in nums {}
| ----
| |
| `nums` moved due to this implicit call to `.into_iter()`
| help: consider borrowing to avoid moving into the for loop: `&nums`
4 | for _ in nums {}
| ^^^^ value used here after move
|
note: this function consumes the receiver `self` by taking ownership of it, which moves `nums`
即使您通过可变引用获取迭代器,迭代一次也会耗尽它,第二次使用时不会留下任何值。
您需要克隆迭代器:
use quote::quote; // 1.0.8
fn example() {
let nums = std::iter::empty::<i32>();
let nums2 = nums.clone();
quote! {
#(#nums)*
#(#nums2)*
};
}
您还可以将迭代器收集到一个 Vec
中并对其进行多次迭代:
use quote::quote; // 1.0.8
fn example() {
let nums = std::iter::empty();
let nums: Vec<i32> = nums.collect();
quote! {
#(#nums)*
#(#nums)*
};
}