I have created a trait that is basically meant to wrap a type that I can get in a send/sync way.

```
pub trait ParamBindingGet<T>: Send + Sync {
fn get(&self) -> T;
}
```

and I’m creating a bunch of implementations of this for transformations/combinations… for instance, adding or multiplying 2 values.

```
/// Sum two numeric bindings.
pub struct GetSum<T, L, R> {
left: Arc<L>,
right: Arc<R>,
_phantom: PhantomData<fn() -> T>,
}
/// Multiply two numeric bindings.
pub struct GetMul<T, L, R> {
left: Arc<L>,
right: Arc<R>,
_phantom: PhantomData<fn() -> T>,
}
impl<T, L, R> GetSum<T, L, R>
where
T: Send,
L: ParamBindingGet<T>,
R: ParamBindingGet<T>,
{
/// Construct a new `GetSum`
///
/// # Arguments
///
/// * `left` - the binding for left value of the sum
/// * `right` - the binding for the right value of the sum
pub fn new(left: Arc<L>, right: Arc<R>) -> Self {
Self {
left,
right,
_phantom: Default::default(),
}
}
}
impl<T, L, R> ParamBindingGet<T> for GetSum<T, L, R>
where
T: std::ops::Add + num::Num,
L: ParamBindingGet<T>,
R: ParamBindingGet<T>,
{
fn get(&self) -> T {
self.left.get().add(self.right.get())
}
}
impl<T, L, R> GetMul<T, L, R>
where
T: Send,
L: ParamBindingGet<T>,
R: ParamBindingGet<T>,
{
/// Construct a new `GetMul`
///
/// # Arguments
///
/// * `left` - the binding for left value of the multiplication
/// * `right` - the binding for the right value of the multiplication
pub fn new(left: Arc<L>, right: Arc<R>) -> Self {
Self {
left,
right,
_phantom: Default::default(),
}
}
}
impl<T, L, R> ParamBindingGet<T> for GetMul<T, L, R>
where
T: std::ops::Mul + num::Num,
L: ParamBindingGet<T>,
R: ParamBindingGet<T>,
{
fn get(&self) -> T {
self.left.get().mul(self.right.get())
}
}
```

It is nice that I can simply implement the trait for only the bounds that are important for say, `Add`

or `Mul`

, but really these are just binary operators and could be combined if I just had the type specify an operation and then further constrain the bounds to include `Add + Mul`

etc.

something like:

```
pub enum BinaryMathOp<L, R, T> {
Add { left: L, right: R },
Mul { left: L, right: R}
}
impl<T, L, R> ParamBindingGet<T> for BinaryMathOp<T, L, R>
where
T: num::Num + std::ops::Add + std::ops::Mul,
L: ParamBindingGet<T>,
R: ParamBindingGet<T>,
{
fn get(&self) -> T {
//maybe not quite right, but you get the idea
match self {
&Add { left, right } => left.get().add(right.get())
&Mul { left, right } => left.get().mul(right.get())
}
}
}
```

So, maybe I’m over-engineering things and this is just fine but I wonder if there is a better way? Is there a way that I can implement the trait I care about for each operation, with only the bounds that that operation needs? With `T: Add`

just for the bounds that `Add`

needs? and do the same for `Mul`

, `Div`

(with some additional divide by zero checking) etc without having to have an individual struct per operation?

Also, in a side note, is there some nice way to specify the `phantom`

in this case so that all of the operators are initialized in the same way? `Add { left, right}`

, `Mul { left, right }`

…