【问题标题】:How do I write a macro that returns the implemented method of a struct based on a string?如何编写一个宏来返回基于字符串的结构的实现方法?
【发布时间】:2020-11-11 17:01:06
【问题描述】:

How to get struct field names in Rust? 的启发,我想获得一个基于str 的结构实现方法,类似于:

macro_rules! comp {
    (struct $name:ident {
        $($field_name:ident : $field_type:ty,)*
    }
    impl $name2:ident {
        $(pub fn $func_name:ident($($args:tt)*) $bk:block)*
    }

    ) => {
        //basic component
        struct $name {
            $($field_name: $field_type),*
        }
        impl $name {
            $(pub fn $func_name($($args)*) $bk)*

            // the generated function
            pub fn get_method(index: &str) -> &'static dyn Fn() {
                $(if stringify!($func_name) == index {
                    return $func_name;                               // (***)
                })*
                //
                // Some code here to return the right function
                //
            }
        }
    };
}

fn main() {
    comp! {
        struct S {
            field: String,
        }
         impl S {
            pub fn method1() {
                println!("method1 called");
            }
            pub fn method2() {
                println!("method2 called");
            }
        }
    }
    // the functionality should achieved
    // S::get_method("method1") == S::method1
    // S::get_method("method2") == S::method2
}

本来想用return $func_name获取函数指针,但是好像不可能;标有(***) 的代码行出现错误:

error[E0423]: expected function, found macro `stringify`
  --> src/main.rs:19:22
   |
19 |                   $(if stringify($func_name) == index {
   |                        ^^^^^^^^^ not a function
...
31 | /     comp! {
32 | |         struct S {
33 | |             field: String,
34 | |         }
...  |
42 | |         }
43 | |     }
   | |_____- in this macro invocation
   |
   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
help: use `!` to invoke the macro
   |
19 |                 $(if stringify!($func_name) == index {
   |                               ^

error[E0425]: cannot find value `method1` in this scope
  --> src/main.rs:36:20
   |
36 |             pub fn method1() {
   |                    ^^^^^^^ not found in this scope

error[E0425]: cannot find value `method2` in this scope
  --> src/main.rs:39:20
   |
39 |             pub fn method2() {
   |                    ^^^^^^^ not found in this scope

error[E0308]: mismatched types
  --> src/main.rs:19:19
   |
19 |                   $(if stringify($func_name) == index {
   |  ___________________^
20 | |                     return $func_name;                               // (***)
21 | |                 })*
   | |_________________^ expected reference, found `()`
...
31 | /     comp! {
32 | |         struct S {
33 | |             field: String,
34 | |         }
...  |
42 | |         }
43 | |     }
   | |_____- in this macro invocation
   |
   = note: expected reference `&'static (dyn std::ops::Fn() + 'static)`
              found unit type `()`
   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)

如何完成?

【问题讨论】:

标签: rust macros rust-macros


【解决方案1】:

首先,我要感谢@Shepmaster,如果您的问题并不复杂,您的建议适用于大多数情况。但是需要处理struct,这是用户提供的代码块,在这种情况下,我想出的唯一方法是使用宏规则来实现这一点

一段时间后,我终于完成了。 在我们的例子中,简而言之,我们可以做到

fn get_method(ind: &str) -> Option<&dyn Fn()> {
    // the default type of $name::$func is fn(), function pointer
    // it is recommended to convert into &dyn Fn() if you impl many methods
    let methods = vec![ $(&$name::$func as &dyn Fn()),* ];
    let inds = vec![$(stringify!($func)),*];
    let mut i = 0;
    for item in inds.iter() {
        if item == &ind {
            break;
        } else {
            i +=1;
        }
     }
     if i <= inds.len() - 1 {
         Some(methods[i])
     } else {
         None
     }
     
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-04
    • 2012-04-03
    • 1970-01-01
    • 1970-01-01
    • 2011-12-06
    相关资源
    最近更新 更多