culit - Custom Literals in Rust
You probably know that numbers in Rust can be suffixed to specify their type, e.g. 100i32.
But did you know that syntactically any literal can have a suffix? And did you know that the suffix can be whatever you want?
This crate provides an attribute macro #[culit] for "Custom Literals". When applied to any statement, it enables using custom literals in that statement.
[]
= "0.3"
Note: culit does not have any dependencies such as syn or quote, and it is a simple mapping SourceCode -> SourceCode, so compile-speeds will be very fast.
Example
A NonZeroUsize literal that fails to compile if it is 0: 100nzusize
use culit;
use NonZeroUsize;
IDE Support
Hovering over the custom literals shows documentation for the macro that generates them. You can also do "goto definition". It's quite nice!

More Examples
Python-like f-strings: "hello {name}"f
use culit;
use Duration;
Duration literals: 100m, 2h...
use culit;
use Duration;
The possibilities are endless!
Details
#[culit] replaces every literal that has a custom suffix with a call to the macro
at crate::custom_literal::<type>::<suffix>!($value), where $value is the literal with the suffix stripped:
| literal | expansion |
|---|---|
100km |
crate::custom_literal::int::km!(100) |
70.008e7feet |
crate::custom_literal::float::feet!(70.008e7) |
'a'ascii |
crate::custom_literal::char::ascii!('a') |
b'a'ascii |
crate::custom_literal::byte_char::ascii!(b'a') |
"foo"bar |
crate::custom_literal::str::bar!("foo") |
b"foo"bar |
crate::custom_literal::byte_str::bar!(b"foo") |
c"foo"bar |
crate::custom_literal::c_str::bar!(c"foo") |
Notes:
- Built-in suffixes like
usizeandf32do not expand, so you cannot overwrite them. - Escapes are fully processed, so there's no
raw_byte_str.rb#"f\oo"#just becomesb"f\\oo"
Skeleton
Here's a skeleton for the custom_literal module which must exist at crate::custom_literal.
This module adds a new literal for every type of literal:
Nightly
You need to use #[culit] attribute everywhere you want to use these literals. On nightly, you can apply it on the module:
While this works, I wouldn't recommend it - currently rust-analyzer is unable to properly work with custom inner attributes
that modify the whole crate. For example, if you write 0nzusize which produces a compiler error, the span of the error will point to
the macro crate::custom_literal::int::nzusize but not the actual 0nzusize, which makes it very hard to debug these