What's the magic of this *?

#1

This is a piece from the cook book. I don’t know what the * in *elem -6 does, please see the comments down below. Thanks.

extern crate ndarray;
use ndarray::Array;

fn main() {
let a = Array::from_vec(vec![1., 2., 3., 4., 5.]);
let b = Array::from_vec(vec![5., 4., 3., 2., 1.]);

let z = a + b;

let mut c = Array::from_vec(vec![1., 2., 3., 4., 5.]);
let mut d = Array::from_vec(vec![5., 4., 3., 2., 1.]);

let w = &c + &d;

let epsilon = 1e-8;
for elem in z.iter() {
    println!("elem is {}", elem);  //don't need * here
    let diff: f32 = *elem - 6.;  //without * there is a type mismatch between f32 and f64
    assert!(diff.abs() < epsilon);
}

println!("c is {}", c);
c[0] = 10.;
d[1] = 10.;

for elem in w.iter() {
    let diff: f32 = *elem - 6.;
    assert!(diff.abs() < epsilon);
}  

}

#2

.iter() iterates by reference. That is, instead of giving you directly a number from the array, it gives you a reference: “the number you want is over there”.

* is a dereferencing operator. When you have a reference, * on it takes the value the reference is pointing to.

Rust automatically does this in many situations, like calling methods. But sometimes you need to nudge it to say you want to subtract numbers, not reference to a number.

In your case you can use .iter().cloned() which instead of references to numbers in the array will copy them out and give actual numbers (copying of numbers is super cheap, so that makes sense for arrays of integers or floats).

1 Like
#3

I suspected it, yes I know dereferencing, but mismatch between f64 and f32 confused me.

Thanks for the suggestion too.

#4

Ah, yes. Rust is picky about type conversions. Conversions between f32/f64 influence program speed and precision of calculations, so Rust wants to force programmers to think exactly where these conversions take place.

number as f32 (or *num_ref as f32) will give you the right type.

1 Like
#5

I experimented and found another interesting / tricky feature that I don’t remember I’ve seen in the book.

let epsilon = 1e-8;
for elem in z.iter() {
    let diff: f64 = elem - 6.;  //why not use f64. works with or w/o *
    assert!(diff.abs() < epsilon);
}

println!("c is {}", c);
c[0] = 10.;
d[1] = 10.;

for elem in w.iter() {
    let diff: f32 = *elem - 6.;  //now this will not work
    assert!(diff.abs() < epsilon); 
}  

so when I changed the type of diff in the first loop, the second stops working. basically,
let x = 1.0;
let y: f32 = x; //ok
let z: f64 = x; //fails

It’s like floating point type is determined the first time it’s used. What is it called?

#6

This is called type inference.

#7

Ok, thanks. I’ve seen that with Vec. Didn’t know floating point does that too. G, too much woodoo syntax!

#8

For numerics, it’s not just floating point, it’s integers as well. If you’re using literals and want to be precise about their type right when they’re written, you can append the type to the literal - e.g. 1.0f32 or 1.0f64 (similarly for integers: 1u64 or 1i8, etc). If you’re using a let binding, you can ascribe the type similarly, i.e. let x: f32 = 1.0. I know some people who prefer to use type annotations in most of their code, although most Rust code that I’ve seen makes heavy use of inference.

1 Like
#9

Only one difference, it could be editor related, I’m using VS code,

let a = 5; //it knows it’s i32
let x = 5.0; //doesn’t know type

//else is same
let b: f64 = a; //mismatch interger and f64, not i32 or u32 yet
let c: u32 = a;
let d: i32 = a; //mismatch u32 and i32

let m:i32 = x; //mismatch i32 and floating type
let n: f32 = x;
let q: f64 = x; //mismatch f32 and f64