我想要一个宏来帮助我生成特征,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() {}