Managing lists of Enabled/Disabled methods

During a computation I have to maintain/update two lists of methods indicating whether a method is enabled or disabled -- the methods have varying signatures. During the computation since I must iterate over the list of enabled methods. For the list I would opt for a HashSet<...> where the inner type should be some function pointer I guess. Question: what's the best idiom for this? Thanks.

Can you give a little more detail about what you're trying to do? A collection of boxed closures will work, but there may be a more appropriate choice depending on the particular algorithm you're trying to implement.

I have a bunch of computation objects like:

struct ComputationObject {
    data: Data,
    enabled: HashSet<function_pointer>,
}
impl ComputationObject {
   pub fn comp_a_alt1(&mut self, args: ArgsA) -> A_Result {
         <...some a computation...>
   }
   pub fn comp_a_alt2(&mut self, args: ArgsA) -> A_Result {
         <...some alternative a computation...>
   }
   pub fn comp_b_alt1(&mut self, args: ArgsB) -> B_Result {
         <...some b computation...>
   }
   pub fn comp_b_alt2(&mut self, args: ArgsB) -> B_Result {
         <...some alternative b computation...>
   }
}

where at the enabled set points to the two (A,B) alternative methods valid at any particular time. Note that the signatures of comp_a_... and comp_b_... methods are different; nonetheless they must be stored in the same HashSet. Hope this clarifies.

Silly question, but can't you just use an enum? Then you could call something.comp_b() and internally it'd use a match to figure out which behaviour to choose from.

1 Like

Not so silly actually; I was also thinking in the same direction... I know in C function pointers are a standard way of getting around; I'm just wondering what the Rust equivalent would be...

How about using a trait object then? Or a function/closure? They're the direct equivalents of what you'd do in C with function pointers... A function pointer and void *user_data pointer is just a poor man's trait object, after all.

You might find it tricky to have the same ComputationObject to return either A_Result or B_Result depending on the internal implementation. You'd need to unify the return types somehow, either by just returning the same type, wrapping the possible return types in an enum, or using another boxed trait object if it's okay to not know the value's concrete type.

Alternatively you could make your ComputationObject generic over the underlying implementation and use multiple impl blocks as mentioned in Generic - Multiple impl block with different T bounds. This will only work if you don't need to "forget" the underlying implementation at runtime (e.g. because you want to store a Vec of ComputationObject<AltA> and ComputationObject<AltB> or need to dynamically switch between them at runtime).

3 Likes

Thanks, I'll come back to this and let you know how it's been resolved.

1 Like