Generic functions with metadata

I'm looking for a clean way to achieve something:

First, I have a bunch of functions foo1, foo2, bar1, bar2, etc.
Next, I want each function to have some metadata bundled with it in a way that can be read at runtime and used when each function is called. I'd also like the metadata to be defined alongside the function to keep things from getting out of sync during refactoring, as well as for easy perusal of the source.

Originally I planned on a struct with both the metadata and a function pointer containing a lambda defining the function right there in the struct. However this hit a snag...The metadata structs are static (one copy per library), and the functions are generic - and only made concrete by consumers of the library, ergo no function pointers.

I've been experimenting with macros, and maybe that's the best way to go.

Any ideas?

I'm not certain of this but it made me think of traits and associated consts. Might be awkward but could be worth looking into.

1 Like

I’m having some trouble visualizing the goal here. Can you show some sample code that demonstrates what you want to do, even if it doesn’t work?

Something like this:

trait MyTrait{
    fn bar(&mut self, m: &Meta);
}

fn go<T: MyTrait>(t: &mut T, n: u16){
    let (m, f): (&Meta, fn(&mut T)) = match n {
        1 => (FOO_1, foo_1),
        2 => (FOO_2, foo_2),
        //etc
        _ => panic!("Invalid n!")
    };
    
    t.bar(m);
    f(t);
}

struct Meta{
    field_1: &'static str,
    field_2: u32,
    //etc
}

static FOO_1: &Meta = &Meta {
  field_1: "foo",
  field_2: 99,
};

fn foo_1(t: &mut impl MyTrait){
    //do foo stuff
}

static FOO_2: &Meta = &Meta {
  field_1: "other foo",
  field_2: 116,
};

fn foo_2(t: &mut impl MyTrait){
    //do other foo stuff
}
1 Like

I could simply put the call to t.bar(m) in each and every function, but I'd prefer not to clutter up those functions (there are a lot of them). MyTrait also has other functions generally useful to each function beyond the metadata function "bar".

Edit: this doesn’t work because generics can’t get called through dyns :confused: Thinking of a workaround...

Edit 2: I think this should work now; I lifted the generic parameter to the trait so that you can call it through a dyn MyTraitCallback<T>


What about something like this (untested)? If it’s too wordy, it can probably serve as as a reasonable basis for building a macro.

trait MyTraitCallback<T:MyTrait> {
    fn meta(&self)->&'static Meta { Self::META }
    fn call(&self, t: &mut T);
}

struct Foo1;
impl<T:MyTrait> MyTraitCallback<T> for Foo1 {
    fn meta(&self)->&'static Meta {
        &Meta {
            field_1: "foo",
            field_2: 99,
        }
    }

    fn call(&self, t: &mut T){
        //do foo stuff
    }
}

fn go<T: MyTrait>(t: &mut T, n: u16) {
    let cb:&dyn MyTraitCallback<T> = match n {
        1 => &Foo1,
        /* ... */
        _ => panic!()
    };
    
    t.bar(cb.meta());
    cb.call(t);
}

Thanks. Although I'm not sure I'm comfortable with having dynamic dispatch here.

I suspect it’ll get optimized out because the actual function that needs to be called is known within the function, but there’s obviously no guarantees.