Problem using a generic implementation of binary operators

The following example:

// Sub
use std::ops::Sub;
//
// FloatType
#[derive(Debug)]
struct FloatType<F>(pub F);
//
// &FloatType<F> - &FloatType<F> 
impl<F> Sub< &FloatType<F> > for &FloatType<F>
where
    for<'a> &'a F : Sub<&'a F, Output=F>,
{   
    type Output = FloatType<F>;
    fn sub(self, rhs : &FloatType<F> ) -> FloatType<F> {
        FloatType( (self.0).sub(&rhs.0) )
    }
}
//
// difference
fn difference<V>(x : &V, y : &V) -> V
where
    for<'a> &'a V : std::ops::Sub<&'a V, Output=V> ,
{   x - y 
}
//
// main
fn main() {
    let x  = FloatType::<f32>( 1f32 );
    let y  = FloatType::<f32>( 2f32 );
    let z  = difference(&x, &y);
    println!("z = {:?}", z);
}

results in the following compiler output:

... snip ...
   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`package`)
note: required for `&'a FloatType<std::collections::HashSet<_, _>>` to implement `for<'a> std::ops::Sub`
  --> src/main.rs:9:9
   |
 9 | impl<F> Sub< &FloatType<F> > for &FloatType<F>
   |         ^^^^^^^^^^^^^^^^^^^^     ^^^^^^^^^^^^^
10 | where
11 |     for<'a> &'a F : Sub<&'a F, Output=F>,
   |                                -------- unsatisfied trait bound introduced here
... snip ...

The problem goes away if I change the implementation for the Sub trait to

impl Sub< &FloatType<f32> > for &FloatType<f32>
{   
    type Output = FloatType<f32>;
    fn sub(self, rhs : &FloatType<f32> ) -> FloatType<f32> {
        FloatType( (self.0).sub(&rhs.0) )
    }
}

Is there a generic way to implemen the Sub trait for this case ?

essentially the issue seems to be that F can be T &T &&T &&&T and so on and your implementations tries to recursively apply to all of them.

i think you need to split the trait for owned types from that for references so that references depend on owned and can have at most one level of referencing

Looks like

Works with the next solver.

In the meanwhile there are a couple workarounds for the OP that don't change the impl.

     let x  = FloatType::<f32>( 1f32 );
     let y  = FloatType::<f32>( 2f32 );
-    let z  = difference(&x, &y);
+    let z  = difference::<FloatType<f32>>(&x, &y);
     println!("z = {:?}", z);
-fn difference<V>(x : &V, y : &V) -> V
+fn difference<V>(x: V, y: V) -> V::Output
 where
-    for<'a> &'a V : std::ops::Sub<&'a V, Output=V> ,
+    V: std::ops::Sub,
 {
use std::ops::Sub;
#[derive(Debug, Clone)]
struct FloatType<F>(pub F);
impl<F> Sub for FloatType<F> 
where 

F: Sub<Output = F>, 
{
type Output = Self;

fn sub(self, rhs: Self) -> Self::Output {
  
    FloatType(self.0 - rhs.0)
}
}


fn difference<V: std::ops::Sub<Output = V> + Clone>(x : V, y : V) -> V

{
 x-y

}

fn main() {
let x  = FloatType::<f32>( 1f32 );
let y  = FloatType::<f32>( 2f32 );
let z  = difference(x, y);
println!("z = {:?}", z);
}

Thanks for poniting out that F : Sub<Output=F> does not have the problem.

Unfortunately, for my real world application, F may not implement Copy. It may be vector of floats (of any length) and the operations is element wise. It is necessary to have the borrow form of the Sub operator; i.e., the values passed in should not transfer ownership.

use std::ops::Sub;
#[derive(Debug)]
struct FloatType<F>(pub F);
impl<F> Sub for FloatType<F> 
where 

F: Sub<Output = F>, 
{
type Output = Self;

fn sub(self, rhs: Self) -> Self::Output {

FloatType(self.0 - rhs.0)
}
}
impl<'a, 'b, F> Sub<&'b FloatType<F>> for &'a    FloatType<F>
where


for<'x, 'y> &'x F: Sub<&'y F, Output = F>,
{
type Output = FloatType<F>;

fn sub(self, rhs: &'b FloatType<F>) -> Self::Output {
    
    FloatType((&self.0).sub(&rhs.0))
 }
}

fn difference<V: std::ops::Sub<Output = U> , U>(x : V, y : V) -> U

{
x-y

}

fn main() {
let x  = FloatType::<f32>( 1f32 );
let y  = FloatType::<f32>( 2f32 );
let z  = difference(&x, &y);
println!("z = {:?}", z);
}

Very interesting. The original code works If one only changes the definition of difference (as edddd suggested above); i.e, the folliowing works:

// Sub
use std::ops::Sub;
//
// FloatType
#[derive(Debug)]
struct FloatType<F>(pub F);
//
// &FloatType<F> - &FloatType<F>
impl<F> Sub< &FloatType<F> > for &FloatType<F>
where
    for<'a> &'a F : Sub<&'a F, Output=F>,
{
    type Output = FloatType<F>;
    fn sub(self, rhs : &FloatType<F> ) -> FloatType<F> {
        FloatType( (self.0).sub(&rhs.0) )
    }
}
//
// difference
fn difference<V, U>(x : V, y : V) -> U
where
    V : Sub<Output = U>,
{ x-y }
//
// main
fn main() {
    let x  = FloatType::<f32>( 1f32 );
    let y  = FloatType::<f32>( 2f32 );
    let z  = difference(&x, &y);
    println!("z = {:?}", z);
}

Sorry for bad english, you forgot to put the lifetime of difference so the compiler can't deduce de type correct, it is deducing &FloatType instead FloatType . It try &&FloatType and so on, if you wanna use your original code you can try this

// Sub
use std::ops::Sub;
// FloatType
#[derive(Debug)]
struct FloatType<F>(pub F);
//
// &FloatType<F> - &FloatType<F> 
impl<F> Sub< &FloatType<F> > for &FloatType<F>
 where
 for<'a> &'a F : Sub<&'a F, Output=F>,
 {   
 type Output = FloatType<F>;
 fn sub(self, rhs : &FloatType<F> ) ->    FloatType<F> {
    FloatType( (self.0).sub(&rhs.0) )
 }
 }
//
// difference
fn difference<'b, V>(x : &'b V, y : &'b V) -> V
 where
for<'a> &'a V : std::ops::Sub<&'a V, Output=V> ,
{   x - y 
}
//
// main
fn main() {
let x  = FloatType::<f32>( 1f32 );
let y  = FloatType::<f32>( 2f32 );
let z  = difference::< FloatType::<f32>>(&x, &y);
println!("z = {:?}", z);
}

The code I posted above works on my system; see below

package>cat src/main.rs
// Sub
use std::ops::Sub;
//
// FloatType
#[derive(Debug)]
struct FloatType<F>(pub F);
//
// &FloatType<F> - &FloatType<F>
impl<F> Sub< &FloatType<F> > for &FloatType<F>
where
    for<'a> &'a F : Sub<&'a F, Output=F>,
{
    type Output = FloatType<F>;
    fn sub(self, rhs : &FloatType<F> ) -> FloatType<F> {
        FloatType( (self.0).sub(&rhs.0) )
    }
}
//
// difference
fn difference<V, U>(x : V, y : V) -> U
where
    V : Sub<Output = U>,
{ x-y }
//
// main
fn main() {
    let x  = FloatType::<f32>( 1f32 );
    let y  = FloatType::<f32>( 2f32 );
    let z  = difference(&x, &y);
    println!("z = {:?}", z);
}
package>cargo run
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.02s
     Running `target/debug/package`
z = FloatType(-1.0)
package>cargo --version
cargo 1.92.0 (344c4567c 2025-10-21)
package>

In addition, if I add the function

fn print_type_of<T>(_: &T) {
    println!("{}", std::any::type_name::<T>());
}

just before main, and add

print_type_of( &z );

In main before the println!, I get that the type for z is package::FloatType<f32>