Be able to pass const to procedural macro and act upon

I am trying to write a procedural macro that can accept a constant and this macro will produce code using this constant in its execution. As far as I know, many examples online are with literal arguments.

Theoretically, I think it should be possible because AST should hold all related information.

Here is an example:

extern crate proc_macro;

use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, Expr, ExprLit};

#[proc_macro]
pub fn float_to_string(input: TokenStream) -> TokenStream {
    // Parse the input token stream into an AST
    let input: Expr = parse_macro_input!(input as Expr);

    // Check if the input expression is a float literal
    let float_literal = if let Expr::Lit(ExprLit { lit: syn::Lit::Float(_), .. }) = input {
        true
    } else {
        false
    };

    // If the input is a float literal, replace it with a string representation
    // of the float. Otherwise, return the input as-is.
    let output = if float_literal {
        let float_string = input.to_string();
        quote! {
            #float_string.to_string()
        }
    } else {
        quote! {
            #input
        }
    };

    // Return the modified AST as a token stream
    TokenStream::from(output)
}

This is how you'd call:

float_to_string![3.14]; // returns "3.14"

I want to be able to do this:

const PI: f64 = 3.14; // Can be declared in other module or crate.
float_to_string![PI]; // returns "3.14"

In some other language it may be possible, sure. Not in Rust.

It doesn't matter where PI is declared. If it's not between opening brace [ and closing brace ] then macro (including proc macro) have no idea about it.

Proc macro very explicitly only deals with what's passed to it, it doesn't have access to full AST tree.

It may leave some construct behind that would at runtime do something to PI but they can not do anything to it while macro is expanding.

1 Like

The const_format crate may be able to do what you need. It's got a number of limitations due to the current const evaluation restrictions though.

1 Like

Note that it does precisely what I have said it may do: constexpr expression which would then be executed by compiler.

I just forgot that you can leave not just runtime-evaluated expressions, but const expression which would be evaluated by compiler at compile-time, not runtime.

I have even done similar things in C++, but had no idea Rust constexpr evaluator is good enough to do something like that. My bad, sorry about that.

That's great! Thank you for the pointer. My example was the proximity of what I needed but not exactly the code I was about to use. I am still trying to figure out how const_format achieves const evaluation under the hood. I wonder if I can use it in proc_macro context and if the const values are available.

The crate mostly consists of workarounds due to features that aren't currently available in const fns. Eventually it should be possible to do what you want in a plain old const fn, in theory at least.

Just use cargo expand and it would show you what it actually does.

Sure, why not.

Available when? Of course macros (including proc_macros) in that crate couldn't access what doesn't exist (const values are generated by compiler after all macros are expanded, they don't exist when const_format macros are expanded) but it can create constant expression, which would then, at later stages of the compilation, when constant expressions are calculated, produce the desired value.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.