Created
November 24, 2024 10:47
-
-
Save sug0/1cd6eb2a0a32de7c18bbf5e5b7f72095 to your computer and use it in GitHub Desktop.
Constant type strings in stable Rust in <150 LOC
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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