有没有办法获取宏中变量的类型?

2023-12-25

我有一个程序属性宏,它给定一个函数对每个二进制表达式进行操作,例如let a = b + c;并根据它返回另一个表达式。随着+操作取决于类型,它需要知道类型a, b and c.

有没有办法在编译时获取变量的推断类型?

(就像 rust-analysisr 可能会显示推断的类型一样,我可以在宏中获取这些类型吗?)

简单的例子 -

为了在 Rust 游乐场中更简洁地说明我的方法,我们可以使用声明性宏来调用给定变量上的函数,该函数的细节基于给定变量的类型。

我在 Rust 游乐场中最接近我想要的功能:

macro_rules! SomeMacro {
    ($x:expr) => {{
        $x.some_function(3.)
    }};
}
trait SomeTrait {
    fn some_function(&self,x:f32) -> f32;
}
impl SomeTrait for u32 {
    fn some_function(&self,x:f32) -> f32 {
        x * 3.
    }
}

fn main() {
    let a = 3u32;
    let b = SomeMacro!(a);
    assert_eq!(b,9.);
}

我怎样才能让事情像这样工作:

fn some_function<u32>(x:f32) -> f32 {
    3. * x
}
fn some_function<u32,i8,f32>(x:f32) -> f32 {
    3. * x
}
fn main() {
    let a = 3u32;
    let b = <type_of<a>()>::some_function(3.);
    assert_eq!(b,9.);
    let c = 5i8;
    let d = <type_of<a>(),type_of<b>(),type_of<c>()>::some_function(2.);
    assert_eq!(c,6.);
}

综合示例 -.zip https://drive.google.com/file/d/1lT7s9NvdyOaFkgR3NGyrLLMlwThDCrKK/view?usp=sharing

lib.rs
extern crate proc_macro;
use proc_macro::TokenStream;

#[proc_macro_attribute]
pub fn my_attribute_macro(_attr: TokenStream, item: TokenStream) -> TokenStream {
    let ast = syn::parse_macro_input!(item as syn::Item);
    // eprintln!("{:#?}",ast);

    // Checks item is function.
    let mut function = match ast {
        syn::Item::Fn(func) => func,
        _ => panic!("Only `fn` items are supported."),
    };
    let block = &mut function.block;

    // Updates statements
    let statements = block.stmts
        .iter()
        .filter_map(|statement| update_statements(statement))
        .collect::<Vec<_>>();
    block.stmts = statements;

    let new = quote::quote! { #function };
    TokenStream::from(new)
}
fn update_statements(stmt: &syn::Stmt) -> Option<syn::Stmt> {
    let local = match stmt {
        syn::Stmt::Local(local) => local,
        _ => return Some(stmt.clone())
    };
    let init = &local.init;
    let bin_expr = match *init.as_ref().unwrap().1 {
        syn::Expr::Binary(ref bin) => bin,
        _ => return Some(stmt.clone())
    };

    eprintln!("looking at: {:#?}",stmt);
    // 
    None
}
main.rs
use macro_test::*;
// Goals: 
// - Map from `x` being equal to `a+b` to `x` being equal to `a*b` based off `x` being `f32`.
// - Map from `y` being equal to `c+d` to `y` being equal to `c/d` based off `y` being `u32`.
#[my_attribute_macro]
fn my_function(a: f32, b: f32, c: u32, d: u32) {
    let x = a + b;
    let y = c + d;
}

fn main() {
}

其中一张照片看起来像(来自cargo expand --bin macro-test):

looking at: Local(
    Local {
        attrs: [],
        let_token: Let,
        pat: Ident(
            PatIdent {
                attrs: [],
                by_ref: None,
                mutability: None,
                ident: Ident {
                    ident: "y",
                    span: #0 bytes(316..317),
                },
                subpat: None,
            },
        ),
        init: Some(
            (
                Eq,
                Binary(
                    ExprBinary {
                        attrs: [],
                        left: Path(
                            ExprPath {
                                attrs: [],
                                qself: None,
                                path: Path {
                                    leading_colon: None,
                                    segments: [
                                        PathSegment {
                                            ident: Ident {
                                                ident: "c",
                                                span: #0 bytes(320..321),
                                            },
                                            arguments: None,
                                        },
                                    ],
                                },
                            },
                        ),
                        op: Add(
                            Add,
                        ),
                        right: Path(
                            ExprPath {
                                attrs: [],
                                qself: None,
                                path: Path {
                                    leading_colon: None,
                                    segments: [
                                        PathSegment {
                                            ident: Ident {
                                                ident: "d",
                                                span: #0 bytes(324..325),
                                            },
                                            arguments: None,
                                        },
                                    ],
                                },
                            },
                        ),
                    },
                ),
            ),
        ),
        semi_token: Semi,
    },
)

在处理宏之前,编译器不会决定类型。在许多情况下,宏可以更改推断的类型。这就是为什么可以写这样的东西:

let mut xs = vec![];
xs.push(1);

类型推断可以返回并分配正确的类型xs。这是不可能的,如果vec!在扩展之前必须知道类型。您需要以另一种方式解决这个问题。请参阅@PossibleAShrub's 答案了解一种方法。

As Rust 编程语言:宏 https://doc.rust-lang.org/book/ch19-06-macros.html注释(强调):

另外,宏也被扩展在编译器解释代码的含义之前,例如,宏可以在给定类型上实现特征。

您可能想探索Rust 的原生差异化编程支持 https://internals.rust-lang.org/t/native-differential-programming-support-for-rust/9626/27论坛的讨论,特别是链接的后续主题。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

有没有办法获取宏中变量的类型? 的相关文章

随机推荐