我想要一个宏来帮助我生成特征,ExtraData
如果没有给出,则关联类型将被分配给单位类型。
pub trait TestTrait {
type ExtraData;
fn test(&self, _state: &Self::ExtraData) {}
fn test_a(&self, _state: Self::ExtraData) {}
}
pub struct Data;
#[macro_export]
macro_rules! type_or_unit {
() => { () };
($name:ty) => { $name };
}
#[macro_export]
macro_rules! expand {
{
pub struct $example_type:ident;
impl TestTrait for $example_type1:ident {
$(type ExtraData = $extra_state:ty;)?
$($impls:tt)*
}
} => {
pub struct $example_type;
impl TestTrait for $example_type1 {
type ExtraData = $crate::type_or_unit! {$( $extra_state )?};
$($impls)*
}
}
}
fn main() {
expand!{
pub struct Test;
impl TestTrait for Test {
type ExtraData = ();
}
}
}
rust 宏无法扩展,因为:
error: local ambiguity when calling macro `expand`: multiple parsing options: built-in NTs tt ('impls') or 1 other option.
--> src/main.rs:41:13
|
41 | type ExtraData = ();
为什么会发生这种情况以及如何解决这个问题?
最佳答案
1
这感觉像是一种使用声明式宏的奇怪方法。您可以通过以下方法解决这个问题:不匹配 处的模式type_or_unit!
,而是直接匹配expand!
,这意味着创建两个模式块,一个带有type ExtraData = $extra_state:ty;
,一个不带有 。
但是,如果你能详细说明你想要实现什么,为什么你的类型定义在宏模式匹配中,那就太好了。如果所有方法都已经在特征中实现,为什么不直接采用类型和额外的数据类型。如果你需要在特征中手动实现一些功能,为什么要选择编写宏来生成一行代码。
为什么不
pub trait TestTrait {
type ExtraData;
fn test(&self, _state: &Self::ExtraData) {}
fn test_a(&self, _state: Self::ExtraData) {}
}
macro_rules! impl_TestTrait {
($t:ty, $extra:ty) => {
impl TestTrait for $t {
type ExtraData = $extra;
}
};
($t:ty) => {
impl_TestTrait!($t, ());
}
}
struct Test1;
impl_TestTrait!(Test1, i32);
struct Test2;
impl_TestTrait!(Test2);
fn main() {}
cargo expand
#![feature(prelude_import)]
#[prelude_import]
use std::prelude::rust_2021::*;
#[macro_use]
extern crate std;
pub trait TestTrait {
type ExtraData;
fn test(&self, _state: &Self::ExtraData) {}
fn test_a(&self, _state: Self::ExtraData) {}
}
struct Test1;
impl TestTrait for Test1 {
type ExtraData = i32;
}
struct Test2;
impl TestTrait for Test2 {
type ExtraData = ();
}
fn main() {}
基于您的想法的解决方法
pub trait TestTrait {
type ExtraData;
fn test(&self, _state: &Self::ExtraData) {}
fn test_a(&self, _state: Self::ExtraData) {}
}
pub struct Data;
#[macro_export]
macro_rules! expand {
{
pub struct $example_type:ident;
impl TestTrait for $example_type1:ident {
type ExtraData = $extra_state:ty;
$($impls:tt)*
}
} => {
pub struct $example_type;
impl TestTrait for $example_type1 {
type ExtraData = $extra_state;
$($impls)*
}
};
{
pub struct $example_type:ident;
impl TestTrait for $example_type1:ident {
$($impls:tt)*
}
} => {
pub struct $example_type;
impl TestTrait for $example_type1 {
type ExtraData = ();
$($impls)*
}
}
}
expand!{
pub struct Test1;
impl TestTrait for Test1 {
type ExtraData = i32;
}
}
expand!{
pub struct Test2;
impl TestTrait for Test2 {
}
}
cargo expand
#![feature(prelude_import)]
#[prelude_import]
use std::prelude::rust_2021::*;
#[macro_use]
extern crate std;
pub trait TestTrait {
type ExtraData;
fn test(&self, _state: &Self::ExtraData) {}
fn test_a(&self, _state: Self::ExtraData) {}
}
pub struct Data;
pub struct Test1;
impl TestTrait for Test1 {
type ExtraData = i32;
}
pub struct Test2;
impl TestTrait for Test2 {
type ExtraData = ();
}
fn main() {}
|
|