Restrict type inference for closure to trait implementors

I'm trying to make a closure (in the macro-generated code), which must accept any type implementing certain trait (it will be immediately called and dropped, but the concrete type it will be called with is unknown). I can go with the trait objects and do something like this:

let f = |input: &dyn Debug| {
    println!("{:?}", input);
};
f(&obj);

But is it possible to somehow use static dispatch? impl Trait is not supported here, and I'm not sure if I can specify the closure type to reflect this limitation.

A hacky way would be to send a reference to your closure to an empty function that expects &impl Fn(&T) where T: Debug, which will lead to a compilation error if the closure does not match this signature constraint. That error will be a bit cryptic however, so hopefully there is a nicer way...

I'll keep it in mind, but you're right, I'd like the error to point on obj and not on the closure.

Ah yes, and also I misunderstood your problem I think. Sorry, you cannot make a generic closure yet.

No, you were right - I don't need the closure to be generic. As I said, it will be called once, with one concrete type, and then discarded, so if I just leave the type to be inferred, it will be inferred correctly. I just want the compiler to error out if this type happens to not implement the trait, and I can't check this in macro expansion (since I don't know this type yet, just the expression).

Any reason why you can't do

let f = |input| { println!("{:?}", input); }; 
foo(&obj as &dyn Debug);

Or if you just want the check, and no dynamic dispatch

let _check: &dyn Debug = &obj;
let f = |input| { println!("{:?}", input); }; 
foo(&obj);

Looks like we've got an XY problem here, so I'll try to describe in more detail.

The macro (in fact, a proc-macro, but I'll simplify things here) I'm writing will eventually expand to a bunch of write! calls (plus some glue code in between). Here, all substantial arguments to write are provided by macro user. Something like this:

macro_rules! test {
    ($w:expr, $($arg:expr),*) => {
        $(
            write!($w, "arg: {:?}, ", $arg)
        )*
    }
}

The problem is that, if the first argument doesn't implement Write, then every invocation of write would result in a compile error, and I'd like to reduce all that errors into one. For now, there's only one method I know: to pass it into function containing writes, which expect an argument of type impl Write or &dyn Write, so that the only error will be generated on the function boundary.
However, since there are another arguments of unknown quantity and types, I can't use the ordinary function - it I go this way, I must use a closure, to capture everything else I need. And I'd like to avoid the dynamic dispatch here.

Is this ever possible, or I'm asking for too much from the compiler?

macro_rules! test {(
    $w:expr, $($arg:expr),*
) => ({
    fn assert_write<T : Write> (w: &'_ mut T) -> &'_ mut T { w }
    let w: &mut _ = assert_write(&mut $w);
    $(
        write!(w, "arg: {:?}, ", $arg);
    )*
})}

The only isse with this is that it will generate an assert_write function every time the macro is called, so it'd be better if you could factor that out into a #[doc(hidden)] pub element of the frontend crate of your pair of crates (to then use the equivalent of $crate::assert_write(w)).

Just checked in the playground - this doesn't seem to help, write!s generate an error for each anyway.

Oh, that's a bummer. Changing the return type of assert_write to &'_ mut impl Write does seem to "work", though:

macro_rules! test {(
    $w:expr, $($arg:expr),*
) => ({
    fn assert_write (w: impl Write) -> impl Write
    {
        w
    }
    let mut w = assert_write($w);
    $(
        write!(w, "arg: {:?}, ", $arg);
    )*
})}
1 Like

Thanks! Was thinking about some kind of "type guard", but wasn't able to turn it this way.

1 Like