Expand description
§overloaded_literals

Overloaded Literals to construct your datatypes without boilerplate and with compile-time validation.
§Features
- Compile-time validation of literals (with decent compiler errors)
- Supports
bool, signed and unsigned integers, floats and&'static str.
- Supports
- Construct your types without ceremony or boilerplate.
- 100%
no_stdcompatible. - Runs on stable rust. MSRV: 1.65.0
Ships with implementations for std’s various NonZero and Wrapping structs and CStr.
§Usage
Add the overloaded_literals attribute to a function. This will rewrite any literals to calls to a trait with the literal as generic const parameter. Because a trait is used, construction of any desired target type which implements the type happens automatically:
use std::num::NonZeroI8;
use overloaded_literals::overloaded_literals;
#[overloaded_literals]
fn example() {
let three: NonZeroI8 = 3;
let result = three.saturating_mul(2); // <- This '2' also turns into a `NonZero` automatically because of the signature of `saturating_mul`.
let six = 6; // <- And this '6' as well
assert_eq!(result, six);
}
example()Trait implementations can perform compile-time validation (using ‘const evaluation’) on the passed literal. This means that invalid literals are rejected at compile-time with a descriptive error message:
use std::num::NonZeroI8;
use overloaded_literals::overloaded_literals;
#[overloaded_literals]
fn mistake() -> NonZeroI8 {
let oops: NonZeroI8 = 0; // <- compile error 'NonZero integer literal was 0'.
oops.saturating_mul(2)
}
mistake();§Implementing the traits
As an example, here are the trait implementations for a type EvenI32 which ensures that the value it stores is even, similarly to how NonZeroI32 ensures that the contained value is non-zero.
use overloaded_literals::{overloaded_literals, FromLiteralUnsigned, FromLiteralSigned};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct EvenI32(i32);
impl EvenI32 {
fn new(val: i32) -> Option<Self> {
if val % 2 != 0 {
None
} else {
Some(EvenI32(val))
}
}
}
// Called for 0 and positive literals:
impl<const LIT: u128> FromLiteralUnsigned<LIT> for EvenI32 {
const VALID_LITERAL: u128 = {
if LIT % 2 != 0 {
panic!("Odd EvenI32 integer literal")
} else {
LIT
}
};
fn into_self() -> Self {
let raw = <Self as FromLiteralUnsigned<LIT>>::VALID_LITERAL as i32;
EvenI32(raw)
}
}
// Called for negative literals:
impl<const LIT: i128> FromLiteralSigned<LIT> for EvenI32 {
const VALID_LITERAL: i128 = {
if LIT % 2 != 0 {
panic!("Odd EvenI32 integer literal")
} else {
LIT
}
};
fn into_self() -> Self {
let raw = <Self as FromLiteralSigned<LIT>>::VALID_LITERAL as i32;
EvenI32(raw)
}
}
#[overloaded_literals]
fn example() {
let x: EvenI32 = 100;
// let y: EvenI32 = 7; // <- This would cause a compile error :-)
}
example()Another full example, on how to accept a str literal for your datatype, can be found in the documentation of FromLiteralStr.
§Missing features
The following features are currently missing and would be straightforward additions to later versions of the library:
- Support for
charliterals - Support for raw byte str literals (Requires a similar abstraction as TypeStr.)
Re-exports§
Modules§
- type_
float - Lifting of a f64 literal to the type level,
to allow usage of a
const f64in generic const contexts (which is otherwise not allowed on stable Rust currently.) - type_
str - Lifting of a str literal to the type level,
to allow usage of a
const &'static strin generic const contexts (which is otherwise not allowed on stable Rust currently.)
Traits§
- From
Literal Bool - Build your datatype from a boolean literal (
falseortrue). - From
Literal Float - From
Literal Signed - Build your datatype from a signed integer literal (-1, -2, -3, …).
- From
Literal Str - Build your datatype from a
&'static strliteral. - From
Literal Unsigned - Build your datatype from an unsigned integer literal (0, 1, 2, 3, …).
Attribute Macros§
- overloaded_
literals - Attribute macro to overload literals in the function it is used on.