This question already has answers here:
From and Into traits and conversion of usize to f64
(2 answers)
Closed 2 years ago.
I have implemented the following function that calculates the mean of a sequence:
fn mean<T, R>(seq: &[T]) -> R
where R: Div<R, Output=R> + From<T> + From<usize> + Sum<R>
{
let total: R = seq.iter().map(|&x| R::from(x)).sum()
let size = R::from(seq.len())
total / size
}
However, I'm having trouble when converting the usize that is returned from seq.len()
let numbers = vec![10, 20, 30, 40, 50, 60];
let result: f32 = mean(&numbers);
println!("{:?}", result);
5 | fn mean<T, R>(seq: &[T]) -> R
| ----
6 | where R: Div<R, Output=R> + From<T> + From<usize> + Sum<R>
| ----------- required by this bound in `mean`
...
15 | let result: f32 = mean(&numbers);
| ^^^^ the trait `std::convert::From<usize>` is not implemented for `f32`
I'm quite blocked by this since I'm still a beginner with traits. How can I solve this specific problem? (if it can be done)
It is not possible to represent every possible i32 value in f32 format.
That's the reason why From trait implementation provided for f32 supports only i16/u16/i8/u8.
i32 and f32 have the same amount of bytes but usually f32 spent few bytes for exponent so it can't represent all numbers from i32. Single-precision floating-point format
The problem is that f32 does not implement From<usize>. It does implement From<i16>, From<i8>, From<u16> and From<u8>. This is becausef32` cannot represent all the values of a bigger integer exactly.
What you probably want is to use the as conversion, that allows for some precision loss. Unfortunately you cannot use as on generic types, only on primitive types.
You could write a trait to do all the necessary as conversions manually... But of course there is a crate for that! With num_traits::cast::AsPrimititve your code becomes:
fn mean<T, R>(seq: &[T]) -> R
where R: Div<R, Output=R> + Sum<R> + Copy + 'static,
T: AsPrimitive<R>,
usize: AsPrimitive<R>
{
let total: R = seq.iter().map(|&x| x.as_()).sum();
let size = seq.len().as_();
total / size
}
I have added the Copy constraints, that I think you missed in your code, and also the 'static for R that is required for AsPrimitive.
Related
This question already has answers here:
Why can't I use `&Iterator<Item = &String>` as an iterator?
(3 answers)
How do I create a function that accepts an iterator of i32s as either values or references and sums them?
(1 answer)
How to write a Rust function that takes an iterator?
(3 answers)
Closed 2 years ago.
I have a function that takes a &Vector<T>. I want to change it to take an iterator in order to run it on different container/collection/slice types.
It produces an error because the function is called twice.
What I Have So Far
fn operate_on_iterator<'a>(iterator: &impl IntoIterator<Item = i32>) -> i32 {
// This is an example. Please don't tell me to use `.sum`.
let mut sum = 0;
for val in iterator.into_iter() {
sum += val;
}
sum
}
fn caller() -> i32 {
let v = vec![1, 2, 3, 4];
let s1 = operate_on_iterator(&v);
let s2 = operate_on_iterator(&v);
s1 + s2
}
playground
The Error I Get
error[E0507]: cannot move out of `*iterator` which is behind a shared reference
--> src/lib.rs:13:16
|
13 | for val in iterator.into_iter() {
| ^^^^^^^^ move occurs because `*iterator` has type `impl IntoIterator<Item = i32>`, which does not implement the `Copy` trait
Restrictions and notes
I do not want to use dyn because I prefer the slightly larger code size over the performance impact of pointer dereferencing. (Although I will use it for now and will benchmark it once I have both traits and trait objects implemented, i.e. after I have an answer to this question.) Also using dyn also didn't work so far for me.
I have used this answer as a basis. How to write a Rust function that takes an iterator?
My Item implements Clone, Binding, and Drop.
I also tried to implement it using Iterator instead of IntoIterator. Also no luck.
Shepmaster linked to the answer.
For completeness, this is the necessary change:
&impl IntoIterator<Item = i32> -->
impl IntoIterator<Item = &'a i32>
Resulting in this code:
fn operate_on_iterator<'a>(iterator: impl IntoIterator<Item = &'a i32>) -> i32 {
// This is an example. Please don't tell me to use `.sum`.
let mut sum = 0;
for val in iterator.into_iter() {
sum += val;
}
sum
}
I'm trying to implement a generic function in Rust where the only requirement for the argument is that the multiplication operation should be defined. I'm trying to implement a generic "power", but will go with a simpler cube function to illustrate the problem:
use std::ops::Mul;
fn cube<T: Mul>(x: T) -> T {
x * x * x
}
fn main() {
println!("5^3 = {}", cube(5));
}
When compiling I get this error:
error[E0369]: binary operation `*` cannot be applied to type `<T as std::ops::Mul>::Output`
--> src/main.rs:4:5
|
4 | x * x * x
| ^^^^^^^^^
|
= note: an implementation of `std::ops::Mul` might be missing for `<T as std::ops::Mul>::Output`
What does this mean? Did I choose the wrong trait? How can I resolve this?
Let's break down your example a bit:
fn cube<T: Mul>(x: T) -> T {
let a = x * x;
let b = a * x;
b
}
What are the types of a and b? In this case, the type of a is <T as std::ops::Mul>::Output — sound familiar from the error message? Then, we are trying to multiply that type by x again, but there's no guarantee that Output is able to be multiplied by anything!
Let's do the simplest thing and say that T * T needs to result in a T:
fn cube<T: Mul<Output = T>>(x: T) -> T {
x * x * x
}
Unfortunately, this gives two similar errors:
error[E0382]: use of moved value: `x`
--> src/lib.rs:6:9
|
6 | x * x * x
| - ^ value used here after move
| |
| value moved here
|
= note: move occurs because `x` has type `T`, which does not implement the `Copy` trait
Which is because the Mul trait takes arguments by value, so we add the Copy so we can duplicate the values.
I also switched to the where clause as I like it better and it is unwieldy to have that much inline:
fn cube<T>(x: T) -> T
where
T: Mul<Output = T> + Copy
{
x * x * x
}
See also:
How do I implement the Add trait for a reference to a struct?
How to write a trait bound for adding two references of a generic type?
The bound T: Mul does not imply that the result of the binary operator is also of type T. The result type is an associated type of this trait: Output.
The other issue is that before Rust 1.0 the operator traits switched from pass-by-reference to pass-by-value. In generic code this can be a bit of a pain in the butt (for now at least) because these operators consume their operands unless you also require the types to be Copy.
Just for completeness (in case you don't like to require Copy), let me add some information about a possible alternative direction.
For the sake of generic code, authors of "numeric types" are encouraged to provide additional non-consuming implementations of these operator traits so that you don't need Copy or Clone. For example, the standard library already provides the following implementations:
f64 implements Mul< f64>
f64 implements Mul<&f64>
&f64 implements Mul< f64>
&f64 implements Mul<&f64>
Each of these implementations has f64 as the Output type. Making use of these traits directly is not pretty:
fn cube<T>(x: &T) -> T
where
for<'a> T: Mul<&'a T, Output = T>,
for<'a, 'b> &'a T: Mul<&'b T, Output = T>,
{
x * x * x
}
Eventually, we might get some (slightly) higher level traits, which would reduce the noise. For example: T: Mul2 could imply T: Mul<T> + Mul<&T> and &T: Mul<T> + Mul<&T>, but at the time of writing this, the Rust compiler does not seem able to handle this. At least I could not successfully compile the following code:
use std::ops::Mul;
pub trait Mul2
where
Self: Mul<Self, Output = Self>,
Self: for<'a> Mul<&'a Self, Output = Self>,
for<'a> &'a Self: Mul<Self, Output = Self>,
for<'a, 'b> &'a Self: Mul<&'b Self, Output = Self>,
{
}
impl<T> Mul2 for T
where
T: Mul<T, Output = T>,
T: for<'a> Mul<&'a T, Output = T>,
for<'a> &'a T: Mul<T, Output = T>,
for<'a, 'b> &'a T: Mul<&'b T, Output = T>,
{
}
fn cube<T: Mul2>(x: &T) -> T {
x * x * x
}
fn main() {
let c = cube(&2.3);
println!("Hello, world! {}", c)
}
I think it's safe to say that things will improve in this area. For now, the ability to generically implement numeric algorithms in Rust is not as good as I would like it to be.
I have an ASCII string slice and I need to compute the sum of all characters when seen as bytes.
let word = "Hello, World";
let sum = word.as_bytes().iter().sum::<u8>();
I need to specify the type for sum, otherwise Rust will not compile. The problem is that u8 is a too small type, and if the sum overflows the program will panic.
I'd like to avoid that, but I cannot find a way to specify a bigger type such as u16 or u32 for example, when using sum().
I may try to use fold(), but I was wondering if there is a way to use sum() by specifying another type.
let sum = word.as_bytes().iter().fold(0u32, |acc, x| acc + *x as u32);
You can use map to cast each byte to a bigger type:
let sum: u32 = word.as_bytes().iter().map(|&b| b as u32).sum();
or
let sum: u32 = word.as_bytes().iter().cloned().map(u32::from).sum();
The reason why you can't sum to u32 using your original attempt is that the Sum trait which provides it has the following definition:
pub trait Sum<A = Self> {
fn sum<I>(iter: I) -> Self
where
I: Iterator<Item = A>;
}
Which means that its method sum returns by default the same type as the items of the iterator it is built from. You can see it's the case with u8 by looking at its implementation of Sum:
fn sum<I>(iter: I) -> u8
where
I: Iterator<Item = u8>,
I want to implement a generic fibonacci function that works with any type implementing Zero, One, and AddAssign. I first implemented a version that works fine, but is specialized for num::BigUint (see on play.rust-lang.org). I than came up with the following generic implementation (see on play.rust-lang.org):
extern crate num;
use num::{One, Zero};
use std::mem::swap;
use std::ops::AddAssign;
fn fib<T: Zero + One + AddAssign<&T>>(n: usize) -> T {
let mut f0 = Zero::zero();
let mut f1 = One::one();
for _ in 0..n {
f0 += &f1;
swap(&mut f0, &mut f1);
}
f0
}
This doesn't compile:
error[E0106]: missing lifetime specifier
--> src/main.rs:7:34
|
7 | fn fib<T: Zero + One + AddAssign<&T>>(n: usize) -> T {
| ^ expected lifetime parameter
Rust wants me to add a lifetime parameter to AddAssign<&T> but I don't know how to express the lifetime of f1.
You need to use Higher Rank Trait Bounds. This one means basically "For any lifetime 'a, T satisfies the AddAssign<&'a T> trait":
fn fib<T>(n: usize) -> T
where
for<'a> T: Zero + One + AddAssign<&'a T>,
I also had to change the way fib is called because the compiler couldn't figure out the return type, which could be literally any type that implements those traits. Declaring x's type gives sufficient context to the compiler so that it knows what you want.
fn main() {
let x: num::BigUint = fib(10);
// let x = fib::<BigUint>(10); // Also works
println!("fib(10) = {}", x);
}
playground
I tried to implement a small module where I calculate the mean of a vector:
pub mod vector_calculations {
pub fn mean(vec: &Vec<i32>) -> f32 {
let mut sum: f32 = 0.0;
for el in vec.iter() {
sum = el + sum;
}
sum / vec.len()
}
}
As far as I can tell from the compiler error, there are two problems with my code:
error[E0277]: the trait bound `&i32: std::ops::Add<f32>` is not satisfied
--> src/main.rs:6:22
|
6 | sum = el + sum;
| ^ no implementation for `&i32 + f32`
|
= help: the trait `std::ops::Add<f32>` is not implemented for `&i32`
error[E0277]: the trait bound `f32: std::ops::Div<usize>` is not satisfied
--> src/main.rs:9:13
|
9 | sum / vec.len()
| ^ no implementation for `f32 / usize`
|
= help: the trait `std::ops::Div<usize>` is not implemented for `f32`
I'm trying to add a &i32 with a f32 and I'm trying to divide a f32 with an usize.
I could solve the second error by changing the last line to:
sum / (vec.len() as f32)
Is this is actually how a Rust programmer would do this?
Furthermore, I don't really know how to solve the first error. What has to be done and why?
Yes, dereferencing values and converting numeric types is normal in Rust. These conversions help the programmer recognize that edge cases are possible. As loganfsmyth points out:
An i32 can hold values greater than f32 can represent accurately
Unfortunately, the compiler can't tell what's "correct" for your case, so you still have to be on guard.
For what it's worth, I'd write your current implementation using Iterator::sum:
fn mean(items: &[i32]) -> f64 {
let sum: f64 = items.iter().map(|&v| v as f64).sum();
sum / (items.len() as f64)
}
You should also probably handle the case where the input is empty to avoid dividing by zero:
fn mean(items: &[i32]) -> Option<f64> {
let len = items.len();
if len == 0 {
None
} else {
let sum: f64 = items.iter().map(|&v| v as f64).sum();
Some(sum / (len as f64))
}
}
Using the method from What is a good solution for calculating an average where the sum of all values exceeds a double's limits?, but made a bit more iterator-heavy:
fn mean2(ary: &[i32]) -> f64 {
ary.iter().enumerate().fold(0.0, |avg, (i, &x)| {
avg + ((x as f64 - avg) / (i + 1) as f64)
})
}
See also:
Why is it discouraged to accept a reference to a String (&String) or Vec (&Vec) as a function argument?
.iter() returns an &i32 and Rust does not automatically dereference for type conversions — you are currently trying to change the pointer (&) instead of changing what it's pointing to.
Changing your code to look like this is the simplest way to make it work:
pub mod vector_calculations {
pub fn mean(vec: &Vec<i32>) -> f32 {
let mut sum: f32 = 0.0;
for el in vec.iter() {
sum = *el as f32 + sum; // first dereference the pointer, than cast to f32
}
sum / vec.len() as f32 // cast to f32
}
}
But there are some ways to improve this kind of code:
pub mod vector_calculations {
pub fn mean(vec: &[i32]) -> f32 { // accept a slice instead of a vector
// it now allows arrays, slices, and vectors
// but now you can't add or remove items
// during this function call.
let mut sum: i32 = 0; // as the sum is still a whole number, changing the type
// should make it slightly easier to understand.
for el in vec.iter() {
sum = el + sum; // now this works without changing the type of el
// you don't even need to dereference el anymore
// as Rust does it automatically.
}
sum as f32 / vec.len() as f32 // now you need to cast to f32 twice at the end
}
}