Solutions to implement functions which depends on a trait

Hi, I'm trying to write a code using generics... my issue is that I need to to make a math operation inside which means I need to convert from f64 to T, but any lib, T could be implemented on Into or TryInto...., I'm trying to dom something like this:

use std::error::Error;

pub trait TryFloat: TryFrom<f64, Error = Box<dyn Error>> {}
impl<T: TryFrom<f64, Error = Box<dyn Error>>> TryFloat for T {}

pub trait NoTryFloat: From<f64> {}
impl<T: From<f64>> NoTryFloat for T {}

struct A<T> {
        value: T,
}

impl<T: TryFloat> A<T> {
        fn foo(value: T) {
                let t: T = (0.5).try_into().unwrap();
        }
}

impl<T: NoTryFloat> A<T> {
        fn foo(value: T) {
                let t: T = (0.5).into();
        }
}

I know this will not works, and at some point specialization could help here, which options on stable rust we have to do this?

Thx!

All From implementors get a TryFrom implementation automatically, so you can just use TryFrom. The error cases should optimize out if impossible.

struct A<T> {
    value: T,
}

impl<T: TryFrom<f32, Error: Debug>> A<T> {
    fn foo(value: T) {
        let t: T = (0.5).try_into().unwrap();
    }
}

thx for the answer! very useful, and how can we do it if depends on different Traits? non related ones?

Maybe you can't without specialization or newtyping, but honestly, that's just a shot in the dark at what you mean exactly. Can you provide an example (in code)?

1 Like

This is hard to understand.
Usually we say "T could implement Into"

Except, Into and TryInto are generic, so there are two types involved: the type implementing the trait, T and the trait's generic type parameter, U.
I would say
"T could implement Into<U> or TryInto<U>".

  • I just noticed that markdown does not display the <U> unless it is formatted as code using bacticks `Into<U>`

Forgive me if I'm wrong, but it sounds like you want to work with some type T, that could implement TryInto<f32> but not Into<f32> and you want to do something different if T doesn't implement Into<f32>

or f64, either float type same issue.

Does that sound right?

More code examples would also help :slight_smile:

Hi! well this issue has two levels, the first one, features of Rust that are... at some point equals (not completely) and needed for the same code, other example is the Clone/Copy trait, but after reading more, Clone always comes with Copy, so very similar to the TryFrom/From case, while we use clone we will always be able to use generics nicely.

The second level is harder, is when we can perform an action, but can be done different on the traits that are implemented:

struct A {
  val: f64
}

trait MyT1 {
  fn length_from_a(&self) -> f64;
}

trait MyT2 {
  fn length_from_b(&self) -> f64;
}

impl MyT1 for A {
  fn length_from_a(&self) -> f64 {
    10.0
  }
}

// Note MyT2 is not implemented, we could do it and not implement MyT1

struct B<T> {
  val: T
}

impl<T: MyT1> B<T> {
  fn length(&self) -> f64 {
     self.val.length_from_a()
  }
}

impl<T: MyT2> B<T> {
  fn length(&self) -> f64 {
     self.val.length_from_b()
  }
}

This will fails, because length is used in two places, this is basically specialization, but no idea how to do this in other way...., the key point here is that the traits and A can't be changed, can comes from other libs, so the question is how to define B and be able to use any of the traits if is implemented.

What do you want to happen if they are both implemented?

It sounds like this is exactly specialization, and unfortunately you cannot make it work because MyT1 is not more or less specific than MyT2.

As an alternative you could have 2 methods with different names implemented with separate bounds, which isn't particularly satisfactory but should at least compile.

sorry! I thought I answered, because there is several ways to get that value, so even if we use two different names there is no way to know when to call one or other in a superior function.

Other very brute way... implement every trait to the element and make them return Option, so I can call all the functions and found which one is implemented, still is very awful.

Well idk if that helps in your situation (depends on how you actually need to use the trait) but if a macro is acceptable you could try to use autoref specialization

1 Like