Focus of Rust evolution

Some time ago, I summarized all that I would do if I could change the Rust syntax. Considering Rust would get fully dependent types in the future, I imagined standard primitive types could be ranged and then checked at compile time.

Using a more conventional Rust syntax this would look like this:

Primitives with Range

Current primitive types would have their matching ranged types : ISize, I8, … (the old primitive types might be deprecated). All these integer types would be optionally parameterized by any kind of range. The type I32<1..=10> would be a 32 bit signed integer, that is compile time checked to only accept values from 1 to 10. If the range has unbound sides, the limit of these sides will be the hard bounds of the type. The type inference would automatically reduce the range on immutable variables.

Example:

let a = 10;                  // the actual type is I32<10..=10>
let mut b : I32<1..=10> = 4; // b is limited from 1 to 10
let mut c : I32<1..> = 1;    // c is limited from 1 to I32::max
let mut d = 20;              // the actual type is I32 (same as I32<..>)

Arithmetic

All usual arithmetic operators would check, at compile time, there can be no overflow.

The range of the result of an arithmetic operation would be determined by the range of the operands. For instance, an addition between two I32<-5..=5> would return a I32<-10..=10>. If one of the bounds of the result would be over the hard limits of the type, or if the range of a divisor contains 0, there would be a compile time error.

For operations that can't be checked at compile time there would be a whole new set of arithmetic operators: +?, -?, … that return a value with a range truncated to fit the hard limits. These operators would cause an early return (like the ? operator) if there is an overflow or division by zero.

There could be another set of arithmetic operators that panics instead. In my original text, I planned to use +!, -!, … but without the other syntax changes I was proposing, it would conflict with the negation operator. I'll use , , … .

Example (using variables from previous example):

let x = a + b;    // x is a I32<11..=20>
let x = a + c;    // compilation error : x would be I32<11..=I32::max+10>
                  // but the upper bound can't be over I32::max
let x = a +? c;   // Early return on overflow. x is a I32<11..>
let x = a +§ c;   // Panic on overflow. x is a I32<11..>
let x = a / b;    // compilation ok. x is a I32<1..=10>
let x = a / d;    // compilation error : the range of the divisor contains 0
let x = a /? d;   // x is I32<-10..=10>. Early return if d == 0. 

Array indexing

Using bounded integers allows to control array indexing at compile time. Like for arithmetic, there would be new syntax for runtime checked indexing : array[?index] that early return on overflow, and array[§index] that panics on overflow.

Example (using variables from previous example):

let array = [1,2,3,4,5,1,2,3,4,5,1,2,3,4,5]; // array is a [I32<1..=5>;15]
let x = array[b];       // ok since b is I32<1..=10> so inside array size
let x = array[b+10];    // compilation error: b+10 is I32<11..=20> so outside array size
let x = array[?b+10];   // Early return if overflow
let x = array[§b+10];   // Panic if overflow
8 Likes