How can you create nominal typing for primitives without struct wrappering?

Viewed 85

I have two different kinds of floats, Latitude and Longitude. How can I create nominal types for these different floats such that assigning a f32 variable with a semantic meaning of Longitude to a variable with a type and semantic meaning of Latitude is an error? Can I do this without a struct wrappering the primitive type?

type Longitude = f32;
type Latitude = f32;

fn main() {
    let lng : Longitude = 42.0;
    let lat : Latitude = lng; // <-- goal is a type error
    println!("lat={}, lng={}", lat, lng);
}

How can I create nominal types for Rust primitives?

1 Answers

Short answer based on Shepmaster's excellent answer to a related question: You can't. You have to make a struct and define converters to/from that new struct and the underlying type it supports (the converters won't go two steps, so two types compatible with f32 won't convert to each other directly).

For example:

struct Latitude(f32);

impl Latitude {
    fn from_value(v: f32) -> Result<Latitude, ()> {
        if v >= -90.0 && v <= 90.0 {
            Ok(Latitude(v))
        } else {
            Err(())
        }
    }

    fn value(&self) -> f32 {
        self.0
    }
}

It might seem like pointless boilerplate, but in this case, it does provide the added advantage of ensuring the values are sane, where a latitude above 90 degrees is nonsensical, and should rightly be rejected on construction.

Related