Skip to content

Instantly share code, notes, and snippets.

@sug0
Created November 24, 2024 10:47
Show Gist options
  • Save sug0/1cd6eb2a0a32de7c18bbf5e5b7f72095 to your computer and use it in GitHub Desktop.
Save sug0/1cd6eb2a0a32de7c18bbf5e5b7f72095 to your computer and use it in GitHub Desktop.
Constant type strings in stable Rust in <150 LOC
use std::any::type_name;
use std::marker::PhantomData;
use std::ops::Index;
macro_rules! sbytes {
() => { () };
($head:expr $(,)?) => {
Byte<$head, ()>
};
($head:expr, $($tail:expr),* $(,)?) => {
Byte<$head, sbytes!($($tail),*)>
};
}
fn main() {
hello_world();
field_access();
}
fn hello_world() {
println!("HELLO WORLD");
println!("===========");
type S = sbytes!(b'H', b'e', b'l', b'l', b'o', b' ', b'w', b'o', b'r', b'l', b'd', b'!');
println!("Type encoded string: {:?}", type_name::<S>());
println!();
println!("The actual string: {:?}", unsafe {
utf8_bytes_to_str::<S>()
});
println!();
}
fn field_access() {
println!("FIELD ACCESS");
println!("============");
let x = test_struct::Type { abc: 1, def: 2 };
println!("abc = {}", x[test_struct::field::abc::get()]);
println!("def = {}", x[test_struct::field::def::get()]);
println!();
}
const unsafe fn utf8_bytes_to_str<S>() -> &'static str
where
S: Utf8Bytes,
{
std::str::from_utf8_unchecked(std::slice::from_raw_parts(
S::UTF8_BYTES.0.as_ptr(),
S::UTF8_BYTES.1,
))
}
#[derive(Debug)]
struct Byte<const B: u8, Next> {
_next: PhantomData<*const Next>,
}
impl<const B: u8, Next> Byte<B, Next> {
const fn get() -> Self {
Self { _next: PhantomData }
}
}
const MAX_CONST_STR_LEN: usize = 4096;
trait Utf8Bytes {
const UTF8_BYTES: ([u8; MAX_CONST_STR_LEN], usize);
}
impl Utf8Bytes for () {
const UTF8_BYTES: ([u8; MAX_CONST_STR_LEN], usize) = ([0u8; MAX_CONST_STR_LEN], 0);
}
impl<const B: u8, Next> Utf8Bytes for Byte<B, Next>
where
Next: Utf8Bytes,
{
const UTF8_BYTES: ([u8; MAX_CONST_STR_LEN], usize) = const {
let mut new = [0u8; MAX_CONST_STR_LEN];
let new_len = 1 + Next::UTF8_BYTES.1;
if new_len > MAX_CONST_STR_LEN {
panic!("Max constant string length exceeded");
}
new[0] = B;
let mut i = 0;
while i < Next::UTF8_BYTES.1 {
new[i + 1] = Next::UTF8_BYTES.0[i];
i += 1;
}
(new, new_len)
};
}
mod test_struct {
use super::*;
pub struct TestStruct {
pub abc: i32,
pub def: i32,
}
pub type Type = TestStruct;
#[allow(non_camel_case_types)]
pub mod field {
use super::*;
pub type abc = sbytes!(b'a', b'b', b'c');
pub type def = sbytes!(b'd', b'e', b'f');
}
}
impl Index<test_struct::field::abc> for test_struct::Type {
type Output = i32;
fn index(&self, _: test_struct::field::abc) -> &Self::Output {
&self.abc
}
}
impl Index<test_struct::field::def> for test_struct::Type {
type Output = i32;
fn index(&self, _: test_struct::field::def) -> &Self::Output {
&self.def
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment