light_ranged_integers 0.1.0

A Rust library similar to ranged_integers, but working on a stable compiler version
Documentation

Light ranged integers

Ranged integers for stable Rust compiler.

Why

The concept of constraining an integer variable to a specific range comes from solid languages like Ada. In Rust we have the ranged_integers package, which is really powerful, with automatic type size and automatic interval limits extension; however, it currently needs a Nightly compiler along with enabling some experimental compiler features, as it needs more advanced control of constant expressions and generics.

While having more compile time automation is welcome, runtime checking is good enough for most situations. In fact, even in Ada range types are mostly runtime checked, excluding the Range declaration and the very first initialization. If runtime checking is good enough for Ada's high safety standards, it's probably good enough for your use case, but that's up to you to decide.

Compared to Rust ranged_integers package, this project has no automatic type size detection and boundary limit extension/shrinking, so it works on a stable compiler version, has only one dependency (paste macro) and a very small codebase.

Usage

A Range type guarantees that the contained number fits the range from MIN to MAX, extremes included. MIN must be smaller than MAX, or it will refuse to compile.

The library offers Range types, declared in this form

RangedU16<MIN, MAX, OpMode>

where:

  • MIN is a lower limit of the interval
  • MAX is the upper limit of the interval
  • OpMode selects how to behave when a number goes out of range
use light_ranged_integers::{RangedU16, op_mode::Panic};

// Create a value of 3 in a range from 1 to 6, extremes included
let n1 = RangedU16::<1,6, Panic>::new(3);

It's also possible to check the initialization value fits the range at compile time

// Create a value of 3 in a range from 1 to 6, extremes included
// This is checked at compile time
use light_ranged_integers::RangedU16;
let n1 = RangedU16::<1,6>::const_new::<3>();

Modes

When you declare a Ranged type, you can select a behaviour to apply when the number goes outside range. There are currently three modes: Panic, Clamp and Wrap.

Panic

Panic mode is the default. If a number falls outside the range, the program panics.

use light_ranged_integers::{RangedU16, op_mode::Panic};
// Create a ranged u16, interval [1,6], in Panic mode.
let n1 = RangedU16::<1,6, Panic>::new(5);

// As Panic mode is the default, you can also initialize using only the range
let n1 = RangedU16::<1,6>::new(5);

let res = n1+5;// This line will panic, as 5+5=10 is out of range

Clamp

In Clamp mode, a number not fitting the interval is clamped to its closest extreme. For example, a 10 will be clamped to 6 when you try to fit it into a [1, 6] interval.

use light_ranged_integers::{RangedU16, op_mode::Clamp};
// Create a ranged u16, interval [1,6], in Clamp mode.
let n1 = RangedU16::<1,6, Clamp>::new(3);
assert_eq!(n1,3);

// Here 10 is clamped to a 6 to fit into range
let n2 = RangedU16::<1,6, Clamp>::new(10);
assert_eq!(n2,6);

// We're in clamp mode, so 3+6=9 is clamped at 6 automatically
assert_eq!(n1+n2, 6)

Wrap

(WORK IN PROGRESS) Wrap mode. In this mode, out of range numbers are wrapped to fit the interval.

Range limits

Range limits are not expanded or shrinked automatically in order to be able to use stable Rust and to prevent unexpected range change.

use light_ranged_integers::{RangedU16, op_mode::Clamp};
let n1 = RangedU16::<1,6, Clamp>::new(3);
let n2 = RangedU16::<1,6, Clamp>::new(6);

// Expand the limit of each operand to 1-12
// Then 3+6=9 fits the range and 9 is not clamped
assert_eq!(
    n1.limit::<1,12>()
        +
    n2.limit::<1,12>(),
    9
);

The library offers some additional safety restrictions.

You can't compile code that computes operations in which the operands have different range. We can't assume the desired output range, if it should be the first one, the second one, a shrinked range or an extended range. It's better to not compile and let the user manually set the limits.

use light_ranged_integers::RangedU8;

// Does NOT compile, different ranges
RangedU8::<0,1>::new(0) + RangedU8::<0,2>::new(0)

Also you can't do operations between values with different Modes (i.e. can't add a Clamp mode value with a Panic mode one). We can't assume the desired mode to use, so it's better to not compile at all.

use light_ranged_integers::{RangedU8, op_mode::{Clamp, Panic}};

// Does NOT compile, different mode (Panic vs Clamp)
RangedU8::<0, 1, Panic>::new(0) + RangedU8::<0, 1, Clamp>::new(0)

Copyright

This software is free and open source software, as defined by its license, this is NOT public domain, make sure to respect the license terms. You can find the license text in the COPYING file.

Copyright © 2024 Massimo Gismondi

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see https://www.gnu.org/licenses/.

Dependencies

This project leverages the paste crate, published under either of Apache License, Version 2.0 or MIT license at your option.