How to get "sqrt" working for generic type?

Hi!

Suppose I am defining a struct such as:

use num::Num; // 0.4.0
#[derive(Copy, Clone)]
pub struct Vector<Ti: Num,Tj: Num,Tk:Num> 
{
    i: Ti,
    j: Tj,
    k: Tk,
}

Then I want to take there square root of a vector, such as in this following code:

// Implement funcitonalities
impl<T:Num> Vector<T,T,T> {
    pub fn norm(self) -> T {
       let val = self.i*self.i + self.j*self.j + self.k*self.k;
       return  val.sqrt();
    }
}

This method (sqrt) does not exist and gives the warning:

error[E0599]: no method named `sqrt` found for type parameter `T` in the current scope
  --> src/math_vectors.rs:37:20
   |
37 |        return  val.sqrt();
   |                    ^^^^ method not found in `T`
   |
   = help: items from traits can only be used if the type parameter is bounded by the trait
help: the following traits define an item `sqrt`, perhaps you need to restrict type parameter `T` with one of them:
   |
34 | impl<T: Float + Num> Vector<T,T,T> {
   |      ~~~~~~~~~~
34 | impl<T: Real + Num> Vector<T,T,T> {
   |      ~~~~~~~~~
34 | impl<T: Roots + Num> Vector<T,T,T> {

I know that it will work with "Float", but I want to be able to input numbers without thinking about putting ".", or if there are floats or not.

I guess in reality if I get a case of Vector(1,2.0,3) I want it to default to a float behaviour, but then doing:

(val as Float).sqrt();

Gives the error:

error[E0038]: the trait `Float` cannot be made into an object
   --> src/math_vectors.rs:37:24
    |
37  |        return  (val as Float).sqrt();
    |                        ^^^^^ `Float` cannot be made into an object

Can someone help?

Thanks!

I think you are a bit confused as to how traits work. In particular the statement val as Float has no meaning, you cannot cast like that. I suggest you read the traits chapter from the Rust book.
Also, I'm not sure, but I think Real will cover integers and floating point. So, you should define the trait bound for sqrt as T: Real . The + Num is not necessary since Real covers Num.
Sidenote: in Rust it is considered an anti-pattern (in most cases) to define trait bound in struct definitions.
Rather, define the bounds for specific methods.

1 Like

Using Real from num_traits did not improve it.

Then it does not allow me to define vector such as this:

let a  = Vector::new(5,0,1);
Type annotations needed for `math_vectors::Vector<{integer}, {integer}, {integer}>`

cannot infer type for type `{integer}`

note: multiple `impl`s satisfying `{integer}: num::Num` found in the `num_traits` crate:
      - impl num::Num for i128;
      - impl num::Num for i16;
      - impl num::Num for i32;
      - impl num::Num for i64;
      and 8 more
help: consider specifying the type arguments in the function call: `::<Ti, Tj, Tk>`rustc(E0283)

Which to be fair also did not work with num::Num.

I just really want to be able to input any numeric type and combination i.e.

(5,1.0,2)
(1,1,1)
(0.5,0.2,0.3)

And then it parses it as Float or something like that, since I cannot seem to get automatic parsing of arguments to work.

Well, that's not what the impl you have in the OP does:

impl<T:Num> Vector<T,T,T>

That's a homogeneous vector, since you said it has to be the same type for each of the three coordinates.

If you want them to be allowed to be different, then say so in the impl:

impl<T1: Num, T2: Num, T3: Num> Vector<T1, T2, T3>
1 Like

Okay now i have as such:

use num_traits::real::Real;
#[derive(Copy, Clone)]
pub struct Vector<Ti: Real,Tj: Real,Tk:Real> 
// Where T somewhere here?
{
    i: Ti,
    j: Tj,
    k: Tk,
}

impl<Ti: Real,Tj:Real,Tk:Real> Vector<Ti,Tj,Tk> {
    pub fn new(i: Ti, j: Tj, k: Tk) -> Vector<Ti,Tj,Tk> {
        Vector {i: i,j: j,k: k}
    }
}

// Implement funcitonalities
impl<T:Real> Vector<T,T,T> {
    pub fn norm(self) -> T {
       let val = self.i*self.i + self.j*self.j + self.k*self.k;
       return val.sqrt();
    }
}

Which works when I do:

let a  = math_vectors::Vector::new(5.,1.,2.);

But if I do all ints or make one an int and rest floats it breaks again..

Kind regards

EDIT: I pasted wrong code in, let me fix it 2 sec.

Of course it allows - it just can't infer it automatically. You'd have to specify it manually with the turbofish operator as the error message says.

I'd really really advice you to go and read the Rust Book chapter I mentioned before, and also the rest of the book. Changing your code until it compiles will get you nowhere.

2 Likes