In the code below, process and process_mut perform the same operations and should ideally be generic over the mutability of Context. Is there any way to achieve having just one process and one CallBack type (both of which are parameterized over mut).
so if I understand this correctly, this function only need shared reference to do its own work, but wants to be transparent to the actual type of the argument so it can pass it to other functions?
I don't know the actual use case, but just for this particular example, it's enough to be generic over the argument a, e.g. Deref<Target=Context> could do it, since both &Context and &mut Context implemented Deref<Target = Context>.
so if I understand this correctly, this function only need shared reference to do its own work, but wants to be transparent to the actual type of the argument so it can pass it to other functions?
Yes, this is correct.
I don't know the actual use case, but just for this particular example, it's enough to be generic over the argument a, e.g. Deref<Target=Context> could do it, since both &Context and &mut Context implemented Deref<Target = Context>.
This works, but only for the exact snippet I posted (my bad). Particularly, if callback(a); happens before the print, then this ceases to work :-(. The compiler suggested that I add a Copy constraint too, but that won't help with the &mut Context case.
exactly, and that's the limitation of a type based design. you cannot make a generic type complete "transparent": rust functions are type checked at definition time (as opposed to "instantiation" time, or "monomophizing" time). in the case of mut ref vs shared ref, it might be possible to come up with a solution for specific cases, but it's impossible in general.
I think the deeper problem is the "control inversion" nature of the design, yet rust's type system is not capable for this level of abstractions. if you could avoid the need of "forwarding" the parameter, and limit the scope of the function to just what it was actually suposed to do, then the problem simply doesn't exist! this function don't even need to be replicated or be generic, just a regular reference is good.
so, I think it'd be better to refactor the code, moving away from a callback based design, and maybe hoist the decision of "high level" control flow up to the caller.
but again, without context of the real application, this is very hypothetic and may not be applicable at all. you might just go with a macro based solution if that suits.
an aside
rust generics are unlike C++ templates (I'm talking about it before concept was added to the language). C++ templates are only checked at instantiation time, while at definition time, they just needs to be syntactically correct.
in this regard, C++ template is more like rust's macros than generics.
also, the so-called "perfect forwarding" in C++ is, IMO, a very cumbersome "solution" by abusing several esoteric language rules [1]
template type deduction for rvalue references in the context of reference collapsing, yadiyadiyada âŠī¸