I'm trying to implement generics within my library using the num crate Float
trait, but I'm stuck fighting the compiler. This works:
struct Vector<T> {
data: Vec<T>,
}
trait Metric<T> {
fn norm(&self) -> T;
}
impl Metric<f32> for Vector<f32> {
fn norm(&self) -> f32 {
let mut s = 0.0;
for u in &self.data {
s = s + u * u;
}
s.sqrt()
}
}
But this doesn't:
use num::Float; // 0.2.0
struct Vector<T> {
data: Vec<T>,
}
trait Metric<T> {
fn norm(&self) -> T;
}
impl<T: Float> Metric<T> for Vector<T> {
fn norm(&self) -> T {
let mut s = T::zero();
for u in &self.data {
s = s + u * u;
}
s.sqrt()
}
}
The latter gives me the following error:
error[E0369]: binary operation `*` cannot be applied to type `&T`
--> src/lib.rs:16:23
|
16 | s = s + u * u;
| - ^ - &T
| |
| &T
|
= note: an implementation of `std::ops::Mul` might be missing for `&T`
If I remove the reference and iterate over self.data
instead, I get a
error[E0507]: cannot move out of borrowed content
--> src/lib.rs:15:18
|
15 | for u in self.data {
| ^^^^^^^^^ cannot move out of borrowed content
Let's look closer at the Float
trait. It is defined as:
pub trait Float: NumCast + Num + Copy + Neg<Output = Self> + PartialOrd<Self> {
// ...
}
Diving into the Num
trait, we see:
pub trait Num: Zero + One + NumOps<Self, Self> + PartialEq<Self> {
// ...
}
And deeper into NumOps
pub trait NumOps<Rhs = Self, Output = Self>:
Add<Rhs, Output = Output>
+ Sub<Rhs, Output = Output>
+ Mul<Rhs, Output = Output>
+ Div<Rhs, Output = Output>
+ Rem<Rhs, Output = Output>
{
// ...
}
That means that any type that implements Float
is able to be multiplied by its own type. Now let's turn back to your code. You are iterating over a Vec<T>
, which gives you a reference to each item, a &T
.
You have a &T
and are trying to multiply that by another &T
. Here's a simplified example of that:
fn do_a_thing<T>(a: &T, b: &T)
where
T: Float,
{
let z = a * b;
}
This gives the same error: binary operation `*` cannot be applied to type `&T`
.
The problem is that you only know that you can multiply a T
by another T
. To say that, you have to explicitly dereference the variables. Since Float
also requires Copy
, this will work:
let z = (*a) * (*b);
Applying the same change to your original code causes it to work:
for u in &self.data {
s = s + (*u) * (*u);
}
You can also dereference the iterator variable when pattern matching:
for &u in &self.data {
s = s + u * u;
}
Or you can add another bound that requires that references to your type can be multiplied:
impl<T> Metric<T> for Vector<T>
where
T: Float,
for<'a> &'a T: std::ops::Mul<&'a T, Output = T>,
{
fn norm(&self) -> T {
let mut s = T::zero();
for u in &self.data {
s = s + u * u;
}
s.sqrt()
}
}
You can also add in a bound for AddAssign
and write simpler code in the body:
impl<T> Metric<T> for Vector<T>
where
T: Float + std::ops::AddAssign,
for<'a> &'a T: std::ops::Mul<&'a T, Output = T>,
{
fn norm(&self) -> T {
let mut s = T::zero();
for u in &self.data {
s += u * u;
}
s.sqrt()
}
}
See also:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With