4 ms·
Range types are probably the main thing I miss from the Rust type system. But even without range constraints, wrapper types (called newtypes in Haskell and Rus
by dbrgn 10y ago
Range types are probably the main thing I miss from the Rust type system.
But even without range constraints, wrapper types (called newtypes in Haskell and Rust) are great. Let's say you have a function that accepts a temperature. A temperature is a float, but you want to prevent the user from mixing up celsius and fahrenheit.
This is what newtype looks like in Rust:
struct Celsius(pub f64);
struct Fahrenheit(pub f64);
fn print_celsius(temperature: Celsius) {
println!("Temperature is {}", temperature.0);
}
It will fail to compile if you accidentally pass a Fahrenheit value to `print_celsius`, but at compile time all values are optimized down to plain floats, without any runtime cost.
More programming languages should adopt this concept :)
- krylon 10y agoHuh. That's clever. This approach should also work in other languages, say, C/C++/C#. I don't remember if C uses structural identity for struct types, but it might work.
- jononor 10y agoNo identity or equality concept in C for structs apart from their address, I'm afraid. In C++ one can define a comparison operator, but it has to be implemented manually. Not that you can use the language-provided == on floats anyway, due to precision issues you should always check if the difference is below a specified limit...
- krylon 10y ago> Not that you can use the language-provided == on floats anyway, due to precision issues you should always check if the difference is below a specified limit... Hehe, I've been there. ;-) Fortunately, that code dealt with physical coordinates, so if two points were less than 100µm apart, they were equal for our purposes.
- johncolanduoni 10y agoOne of my favorite features of Scala that Java is missing is Scala's newtype-like functionality. It lets you define types which get erased to another one the JVM level (so there's no unnecessary boxing) but are checked in Scala and can hide their interior type and provide different methods.
- wBI8j2bsb 10y agoThis is exactly what C does. #include <stdio.h> typedef struct { double v; } Celsius; typedef struct { double v; } Fahrenheit; void print_celsius(Celsius temperature) { printf("Temperature is %f", temperature.v); } void compile_error(Fahrenheit temperature) { print_celsius(temperature); } I suspect LLVM will clean this up to plain floats as well.
- simias 10y agoYeah, C and Rust work the same way here (as far as I can tell). Rust can also define type aliases (with the 'type' keyword) but they're not new types much like C typedefs. The only advantage of Rust in this case is that its tuple syntax can be a bit nicer than C structs and the language supports overloading so that you can reimplement the comparison operators for instance (or do more complicated things, for instance if you have a "decibel" type that must have a special addition implementation)
- tom_mellior 10y agoWhether any compiler "cleans this up" depends in the system's ABI; parameter calling conventions are not up to the compiler to decide (unless it can prove that a given function cannot be called from code it does not control). That said, yes, modern ABIs for modern machines usually pass single-element structures of primitive types in registers of the appropriate type. x86 (32-bit) is not modern in this sense: depending on the exact ABI used, these structures might well be passed on the stack or in an integer register.
- jononor 10y agoOr if the calls are inlined. Not sure if say Rust can/does this more aggresively than C or C++ (or vica versa).
- deleted 10y ago[deleted]