Is it possible to implement Debug for Fn type(closure)

I found the post same as this title when I search some problem.
the original post is here.
@ExpHP gives a method using macro in that post , I paste it as follows

/// Extends a (possibly unsized) value with a Debug string.
// (This type is unsized when T is unsized)
pub struct Debuggable<T: ?Sized> {
    text: &'static str,
    value: T,
}

/// Produce a Debuggable<T> from an expression for T
macro_rules! dbg {
    ($($body:tt)+) => {
        Debuggable {
            text: stringify!($($body)+),
            value: $($body)+,
        }
    };
}

// Note: this type is unsized
pub type PolFn = Debuggable<Fn(Rc<Pol>) -> Rc<Pol>>;

impl<T: ?Sized> fmt::Debug for Debuggable<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
    { write!(f, "{}", self.text) }
}

// This makes Debuggable have most methods of the thing it wraps.
// It also lets you call it when T is a function.
impl<T: ?Sized> ::std::ops::Deref for Debuggable<T>
{
    type Target = T;
    fn deref(&self) -> &T { &self.value }
}

fn main() {
    let d: &PolFn = &dbg!(|x| {
        let _ = "random code so you can see how it's formatted";
        assert_eq!(3 * (1 + 2), 9);
        x
    });
    println!("{:?}", d);
}

In the playground this prints:

| x | {
let _ = "random code so you can see how it's formatted" ; assert_eq ! (
3 * ( 1 + 2 ) , 9 ) ; x }

But this method will not print the actual value of a variable come from outside of closure, such as

fn main() {
    let v = 10;
    let d: &PolFn = &dbg!(|x: usize| {
        let _ = "random code so you can see how it's formatted";
        assert_eq!(3 * (1 + 2), 9);
        if x > v {x} else {x * 2}
    });
    println!("{:?}", d);
}

the print result as folllows

| x: usize | {
let _ = "random code so you can see how it's formatted" ; assert_eq ! (
3 * ( 1 + 2 ) , 9 ) ; if x > v {x} else {x * 2} }

the variables 'v' is not be replaced with its actual value, how to make this method become more better that can print the actual value of the variable refered from outside of closure.
please help, thanks.

This isn't possible, because macros don't know if specific identifiers are from the currant context or are captured

Thanks for reply
I donot know how to the rust compiler process macro and closure. I just think if the variable from outside the closure were replaced with its corresponding value before macro substitution, it might be possiable.
Or is there other better method to implement Debug for Fn/closure?

Macros are processed long before typechecking. Only during typechecking does it become known which variables a closure captures. Closures can be thought of as structs that contain a field for each captured variable combined with an implementation of Fn/FnMut/FnOnce. This is not exactly how they are internally implemented as the captured variables become known relatively late, but it is a close approximation. It is not possible to do any code generation during or after typechecking.

1 Like

Thanks for reply
If this method does not work, is there any other better one to implement trait Debug from Fn/closure?

You could make Debuggable.text an owned String, and then have the macro calculate the contents of that string however you like. One option is to take additional arguments that should be included for context.

Thanks for reply.
but I can't fully understanding what your means about Debuggable.txt, could you help me explain more clearly? thanks.

If Debuggable is defined like this instead:

pub struct Debuggable<T: ?Sized> {
    text: String,
    value: T,
}

then the contents of the text field can be calculated at runtime instead of compile time. You can then make your custom dbg macro use something like format! to build it when the closure is created, which will give you the opportunity to read the environment that will be captured by the closure.

Note however, that any such approach will only work with known closures. It's impossible to do something like this with arbitrary closures, since there's no way to find out what variables are captured (you have to specify them manually).

1 Like

I see, thanks. I have to add extra parameter to avoid the problem, but if there is a way without any extra code to save environment variables will be more graceful.

yes, thank for reply

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.