Rust implement try_from for u8 enum - rust

I have the following code where I need to do direct comparisons between the ranks. For example I need to be able to do self as u8 + 1 == other as u8.
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
#[repr(u8)]
pub enum Rank {
Ace = 1,
Two,
Three,
Four,
Five,
Six,
Seven,
Eight,
Nine,
Ten,
Jack,
Queen,
King,
}
impl TryFrom<u8> for Rank {
type Error = ();
// TODO: replace with macro or find better option
fn try_from(v: u8) -> Result<Self, Self::Error> {
match v {
x if x == Rank::Ace as u8 => Ok(Rank::Ace),
x if x == Rank::Two as u8 => Ok(Rank::Two),
x if x == Rank::Three as u8 => Ok(Rank::Three),
x if x == Rank::Four as u8 => Ok(Rank::Four),
x if x == Rank::Five as u8 => Ok(Rank::Five),
x if x == Rank::Six as u8 => Ok(Rank::Six),
x if x == Rank::Seven as u8 => Ok(Rank::Seven),
x if x == Rank::Eight as u8 => Ok(Rank::Eight),
x if x == Rank::Nine as u8 => Ok(Rank::Nine),
x if x == Rank::Ten as u8 => Ok(Rank::Ten),
x if x == Rank::Jack as u8 => Ok(Rank::Jack),
x if x == Rank::Queen as u8 => Ok(Rank::Queen),
x if x == Rank::King as u8 => Ok(Rank::King),
_ => Err(()),
}
}
}
Is there a more efficient way to write this without using a macro and basically writing it all out anyway?.

tl;dr: Yes, there is a way to do this without macros, but it's unsafe. Macros are fine; use num_enum instead.
If you are willing to delve into the realm of unsafe code, you can use std::mem::transmute() to convert the u8 to Rank:
fn try_from(v: u8) -> Result<Self, Self::Error> {
match v {
x if x >= Rank::Ace as u8 && x <= Rank::King as u8 =>
Ok(unsafe { std::mem::transmute(x) }),
_ => Err(()),
}
}
Beware, if the enum values change later and x >= Rank::Ace as u8 && x <= Rank::King as u8 no longer guarantees that the value is a valid enum value, undefined behavior will result if a bad value is converted.
If you take this approach, I would put very obvious warning comments on the definition of Rank so that others (and future-you) know that changing the values without suitably updating the try_from implementation could cause UB.
From the std::mem::transmute() documentation:
transmute is incredibly unsafe. There are a vast number of ways to cause undefined behavior with this function. transmute should be the absolute last resort.
This is a trade-off of saving a mere 11-12 lines of code at the cost of potentially sabotaging yourself later. I'm giving this answer for the sake of completeness, to say "yes, there is a way to do what you ask, but you really shouldn't do it this way."

Related

Mismatched types parameter error on rust generics

expected type parameter T, found type parameter A error display. I have written lifetime implementation code also but it stills doesn't solve the problem. What's wrong I am doing?
fn main() {
let x = 3;
let y = 5.0;
let max_value = max(x, y);
println!("The maximum value is {}", max_value);
}
fn max<T: PartialOrd, A: PartialOrd>(x: T, y: A) -> T {
if x > y {
x
} else {
y
}
}
// fn main() {
// let x = 3;
// let y = 5.0;
// let max_value = max(&x, &y);
// println!("The maximum value is {}", max_value);
// }
// fn max<'a, T: PartialOrd + Copy, A: PartialOrd + Copy>(x: &'a T, y: &'a A) -> &'a T {
// if x > y {
// x
// } else {
// y
// }
// }
T and A do not have to be the same type, so you have two problems.
The first is that you constrain T and A to be PartialOrd, which is the same thing as PartialOrd<Self>. So your actual constraints are T: PartialOrd<T>, A: PartialOrd<A>. This means you can compare the order of T's to other T's and A's to other A's, but x > y compares a T to an A.
Instead, you need to constrain T: PartialOrd<A>. (This also fails, but because of the invocation in main() -- more on that later.)
Second, the function is declared to return T but the else block returns y, which is not a T. Rust is statically typed, so it expects the types to exactly match.
This could be fixed by requiring that A can be converted to T (that is, A: Into<T>) and then you can return y.into() from the else block.
So at this point, we have:
fn main() {
let x = 3;
let y = 5.0;
let max_value = max(x, y);
println!("The maximum value is {}", max_value);
}
fn max<T: PartialOrd<A>, A: Into<T>>(x: T, y: A) -> T {
if x > y {
x
} else {
y.into()
}
}
But now you are left with more problems:
There are no types T and A satisfying T: PartialOrd<A> where T is an integer and A is a float, therefore you cannot call this function with 3 and 5.0 as you do in main().
Likewise, there's no implementation of Into<T> on A for an integer type T and a float type A.
x > y will move x and y, and then you cannot return them later. This is trivially fixed by constraining both T and A to be Copy.
The second issue could be fixed by having an enum that means "either T or A" and returning that instead. The either crate has such a type called Either, which we can use here as Either<T, A>:
use either::Either;
fn main() {
let x = 3;
let y = 5.0;
let max_value = max(x, y);
println!("The maximum value is {}", max_value);
}
fn max<T: PartialOrd<A> + Copy, A: Copy>(x: T, y: A) -> Either<T, A> {
if x > y {
Either::Left(x)
} else {
Either::Right(y)
}
}
(The println! works because Either<T, A> implements Display when both T and A do.)
You are still left with the problem where there's no built-in ordering implementation between integers and floats.
A "hail mary" solution could be to require that T and A can both be converted to f64 and then convert x and y to f64 before comparing them:
use either::Either;
fn main() {
let x = 3;
let y = 5.0;
let max_value = max(x, y);
println!("The maximum value is {}", max_value);
}
fn max<T: Copy + Into<f64>, A: Copy + Into<f64>>(x: T, y: A) -> Either<T, A> {
if x.into() > y.into() {
Either::Left(x)
} else {
Either::Right(y)
}
}
This is the first bit of code we have that actually compiles, and this might be good enough for your purposes. There are still some issues that remain, however:
i64 and u64 cannot be losslessy converted to f64, therefore they do not implement Into<f64>, and so if you change let x = 3; to let x = 3u64; (or 3i64) compilation will again fail.
f64 does not implement Ord because it's possible for there to be two f64 values x and y that are not equal but neither is greater than the other -- if either value is NaN, for example. This won't cause your program to crash, but it may produce an unexpected or incorrect result.
I suspect that this is a learning exercise, so hopefully this answer helps you understand what is wrong with the original code. I would not recommend a function like this in a real-world program; instead, it would be far better to convert both arguments to be of the same Ord-implementing type ahead of time and then you can use the built-in std::cmp::max function (or Ord::max).

Equivalent of __builtin_ia32_addcarryx_u64 in Rust (addition with carry)

There's no much information about __builtin_ia32_addcarryx_u64, only I coud find is this: https://clang.llvm.org/doxygen/adxintrin_8h_source.html
It looks like __builtin_ia32_addcarryx_u64 returns the carry of the addition of 2 u64 numbers as a u8. While I could implement this in Rust, would be nice to have the fast version of it.
What you're looking for is probably the u64::overflowing_add function (which exists for u64 and all other primitive integer types):
let x: u64 = 0x0123456789abcdef;
let y: u64 = 0xfedcba9876543212;
let (sum, carry) = x.overflowing_add(y);
// sum = 1, carry = true
This function returns a tuple with the resulting sum and the carry bit (as a bool), and is implemented using the add_with_overflow compiler intrinsic, so it should be very fast.
If you need to take a carry value as input, you can (as you mentioned in a comment) use the nightly-only u64::carrying_add. It's just implemented in terms of two overflowing_add operations, though, and you can just use these instead on stable Rust if you need it:
impl u64 {
pub const fn carrying_add(self, rhs: u64, carry: bool) -> (u64, bool) {
// note: longer-term this should be done via an intrinsic, but
// this has been shown to generate optimal code for now, and
// LLVM doesn't have an equivalent intrinsic
let (a, b) = self.overflowing_add(rhs);
let (c, d) = a.overflowing_add(carry as u64);
(c, b || d)
}
}

How do I create a non-recursive calculation of factorial using iterators and ranges?

I ran into a Rustlings exercise that keeps bugging me:
pub fn factorial(num: u64) -> u64 {
// Complete this function to return factorial of num
// Do not use:
// - return
// For extra fun don't use:
// - imperative style loops (for, while)
// - additional variables
// For the most fun don't use:
// - recursion
// Execute `rustlings hint iterators4` for hints.
}
A hint to solution tells me...
In an imperative language you might write a for loop to iterate
through multiply the values into a mutable variable. Or you might
write code more functionally with recursion and a match clause. But
you can also use ranges and iterators to solve this in rust.
I tried this approach, but I am missing something:
if num > 1 {
(2..=num).map(|n| n * ( n - 1 ) ??? ).???
} else {
1
}
Do I have to use something like .take_while instead of if?
The factorial is defined as the product of all the numbers from a starting number down to 1. We use that definition and Iterator::product:
fn factorial(num: u64) -> u64 {
(1..=num).product()
}
If you look at the implementation of Product for the integers, you'll see that it uses Iterator::fold under the hood:
impl Product for $a {
fn product<I: Iterator<Item=Self>>(iter: I) -> Self {
iter.fold($one, Mul::mul)
}
}
You could hard-code this yourself:
fn factorial(num: u64) -> u64 {
(1..=num).fold(1, |acc, v| acc * v)
}
See also:
How to sum the values in an array, slice, or Vec in Rust?
How do I sum a vector using fold?
Although using .product() or .fold() is probably the best answer, you can also use .for_each().
fn factorial(num: u64) -> u64 {
let mut x = 1;
(1..=num).for_each(|i| x *= i);
x
}

How do I perform operations on different numeric types while computing the average in an idiomatic Rust manner?

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
}
}

Normalise ASCII numbers to digit numbers

Running example on play.rust-lang.org
fn main() {
show({
let number = b"123456";
for sequence in number.windows(6) {
let product = sequence.iter().fold(1, |a, &b| a * (b as u64));
println!("product of {:?} is {}", sequence, product);
}
});
}
Instead of having an output like "product of [49, 50, 51, 52, 53, 54] is 15312500000" I need the normal numbers in the brackets and the normalized result for the product.
Trying around with - b'0' to subtract the 48 to get the normal digits in line 5 doesn't work, i.e.
a * ((b as u64) -b'0')
or
(a - b'0') * (b as u64)
Seems I'm missing something here, for example I have no idea what exactly are the 'a' and 'b' values in the fold(). Can anyone enlighten me? :)
Looking at the signature of fold, we can see that it takes two arguments:
fn fold<B, F>(self, init: B, f: F) -> B
where F: FnMut(B, Self::Item) -> B
init, which is of some arbitrary type B, and f, which is a closure that takes a B value and an element from the iterator, in order to compute a new B value. The whole function returns a B. The types are strongly suggestive of what happens: the closure f is repeatedly called on successive elements of the iterator, passing the computed B value into the next f call. Checking the implementation confirms this suspicion:
let mut accum = init;
for x in self {
accum = f(accum, x);
}
accum
It runs through the iterator, passing the accumulated state into the closure in order to compute the next state.
First things first, lets put the type on the fold call:
let product = sequence.iter().fold(1, |a: u64, &b: &u8| a * (b as u64));
That is, the B type we want is u64 (that's what our final product will be), and the item type of the iterator is &u8, a reference to a byte.
Now, we can manually inline the definition of fold to compute product to try to clarify the desired behaviour (I'm ignoring the normalisation for now):
let mut accum = 1;
for x in sequence.iter() {
accum = { // the closure
let a: u64 = accum;
let &b: &u8 = x;
a * b as u64
}
}
let product = accum;
Simplifying:
let mut product = 1;
for &b in sequence.iter() {
product = product * (b as u64)
}
Hopefully this makes it clearer what needs to happen: b runs across each byte, and so it is the value that needs adjustment, to bring the ASCII encoded value down to the expected 0..10 range.
So, you were right with:
a * ((b as u64) -b'0')
However, the details mean that fails to compile, with a type error: b'0' has type u8, but b as u64 as type u64, and it's not legal to use - with u64 and u8. Moving the normalisation to happen before the u64 cast will ensure this works ok, since then you're subtracting b (which is a u8) and a u8:
product * (b - b'0') as u64
All in all, the fold might look clearer (and actually work) as:
let product = sequence.iter()
.fold(1, |prod, &byte| prod * (byte - b'0') as u64);
(I apologise for giving you such confusing code on IRC.)
As an alternative to fold, you can use map and MultiplicativeIterator::product. I find that the two steps help make it clearer what is happening.
#![feature(core)]
use std::iter::MultiplicativeIterator;
fn main() {
let number = b"123456";
for sequence in number.windows(6) {
let product = sequence.iter().map(|v| (v - b'0') as u64).product();
println!("product of {:?} is {}", sequence, product);
}
}
You could even choose to split up the resizing from u8 to u64:
sequence.iter().map(|v| v - b'0').map(|v| v as u64).product();
Nowadays, an alternative is product + to_digit: (itertools was used to print the contents of the iterator)
use {itertools::Itertools, std::char};
fn main() {
let number = b"123456";
let sequence = number
.iter()
.map(|&c| u64::from(char::from(c).to_digit(10).expect("not a digit")));
let product: u64 = sequence.clone().product();
println!("product of {:?} is {}", sequence.format(", "), product);
}
(playground)

Resources