During my few months of tinkering with Rust I have translated a few C programs. By way of gaining some Rust coding practice and as a comparison of the performance that is achieved. With excellent results I might add.
Along the way I have translated things like this:
k=1; l=1;
for(i=0;i<=x.fmax;i++){
k*=ppow(x.p[i],z[i]);
l*=ppow(x.p[i],x.n[i]-z[i]);
}
To this:
let mut k = 1;
let mut l = 1;
for i in 0..=self.factors.fmax {
k *= self.factors.p[i].pow(self.z[i]);
l *= self.factors.p[i]
.pow(self.factors.n[i] - self.z[i]);
}
Only to find of course that there are type mismatches all over and that I can write this to get it working:
let mut k = 1;
let mut l = 1;
for i in 0..=self.factors.fmax {
k *= self.factors.p[i as usize].pow(self.z[i as usize] as u32);
l *= self.factors.p[i as usize]
.pow(self.factors.n[i as usize] as u32 - self.z[i as usize] as u32);
}
This already a bit of a pigs ear and hard to read. The compiler of course knows something I did not and suggests I use ".try_into().unwrap()". Which would render such code unreadable.
What I did not know until now is that using "as" can silently corrupt data by losing bits. It not only changes the type it effectively does masking and modulus along the way.
I had naively assumed that "as" would move big things into small things and change signs but also fail with an overflow if there was some damage being done to the data. Turns out that "as" is "unsafe" in that respect.
Having now read the docs properly Casting - Rust By Example I see that is what it is supposed to do. But now I wonder why?
A common case is all this messing from a loop variable that is integer to an array index which has to be usize. Very annoying. Often conceptually that array index is not a size of anything, it's intended to be an integer in it's own right. For example when converting values with a look up table.
I hear the phrase "idiomatic Rust" used a lot. So what is the idiomatic way to deal with these things in Rust? Ways that will not silently trash values.