Arrays and newtype pattern

289 Views Asked by At

I have a type, that can be represented as f32, without NaN or infinity or negative values and with a maximum value, a trivial example could be people heights in meters, so I'm using the new type pattern to represent it:

struct Height(f32);

impl TryFrom<f32> for Height {
    type Error = &'static str;

    fn try_from(value: f32) -> Result<Self, Self::Error> {
        if value.is_infinite() || value.is_sign_negative() || value > 3. {
            return Err("value was infinite");
        }
        Ok(Height(value))
    }
}

What I don't like is when I have to deal with an array of these, because it should be declared like

let vec = vec![Height::try_from(3.).unwrap(), Height::try_from(2.).unwrap()];

adding a lot of boilerplate. Also when I need to pass it to a function that accepts &[f32], it's a bit of a pain to convert.

What's the idiomatic way of handling a situation like this?

Edit: if it's changed 300. to 3., since in the example I'm talking about people heights in meters.

2

There are 2 best solutions below

1
On

Maybe a macro to call Height::try_from(x).unwrap()?

Something like h!(3.0)

As for the function that wants &[f32], to safely convert from [Height] -> [f32] you would have to construct a new slice using the f32's from the Heights which is O(n) so not too fast.

A better question is why does the function want &[f32] and can you change it?

0
On

The most straight forward and idiomatic way is using iterators and map:

let raw_heights = vec![6.7, 5.8, 0.1, 4.4];

let heights: Vec<Height> = raw_heights.into_iter()
    .map(|rh| Height::try_from(rh).unwrap())
    .collect();

See a full working example in the Rust Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=61a42b0a86c64f4a75bf4094da88b301

You could also use macro_rules for that, but it's an overkill.