arrayvec icon indicating copy to clipboard operation
arrayvec copied to clipboard

Const constructor for strings via a macro?

Open aldanor opened this issue 3 years ago • 3 comments

Wonder if it's possible in theory (scratched my head at it for a bit but couldn't quite figure all the details), to have something like:

const S: ArrayString<10> = array_string!("foo");

This would be super cool as it would allow pre-initialized array-strings in const contexts which is currently not doable.

// Without macros, as of this moment, don't think it's possible. // It's most likely (?) doable via a proc-macro (the problem there will be that we don't know the cap), and may also require adding an extra 'raw' const constructor.

aldanor avatar Nov 06 '21 23:11 aldanor

I had to try, and it works today. Everything PoC, but our Rust const language is powerful enough to do this in stable Rust. It's maybe chance that I started with "byte string", it works fine in const as from_str(&str) too.

What's your use case? Since you'd presumably want a mutable string, I'm unsure about the purpose. With that said, an arraystring is just barely more compact than a string literal (u32 length + data means it's 4 bytes + string length as inline data), so could be useful as a non-mutable thing too?

const S1: ArrayString<10> = ArrayString::from_byte_string_const(b"hi there");
const S2: ArrayString<10> = ArrayString::from_byte_string_const(b"hi there this is too long");  // compile error (panic)
const S3: ArrayString<10> = ArrayString::from_byte_string_const(b"Inv\xAAlid");  // compile error panic


pub const fn from_byte_string_const<const N: usize>(b: &[u8; N]) -> Self {
    assert_capacity_limit_const!(CAP);
    if N > CAP {
        panic!("from_byte_string_const: input too long");
    }
    let len = N;
    let mut vec = Self::new_const();
    let mut i = 0;
    while i < len {
        let byte = b[i];
        if byte >= 128 {
            panic!("from_byte_string_const: only supports ascii at this time");
        }
        vec.xs[i] = MaybeUninit::new(byte);
        i += 1;
    }
    // Safety: we know N <= CAP
    vec.len = len as u32;
    vec
}


pub const fn from_str_const(s: &str) -> Self {
    let b = s.as_bytes();
    assert_capacity_limit_const!(CAP);
    let len = b.len();
    let mut vec = Self::new_const();
    let mut i = 0;
    while i < len {
        if i >= CAP {
            panic!("from_str_const: input too long");
        }
        let byte = b[i];
        vec.xs[i] = MaybeUninit::new(byte);
        i += 1;
    }
    // Safety: we know len <= CAP
    vec.len = len as u32;
    vec
}

bluss avatar Nov 07 '21 01:11 bluss

What do you think of #205? We could skip the ArrayVec parts since they are quite limited

bluss avatar Nov 07 '21 10:11 bluss

What's your use case? Since you'd presumably want a mutable string, I'm unsure about the purpose.

In my particular case, storing a bunch of hardcoded (immutable) "known objects" in a const context that (other than ArrayString, as of now), can be const-constructed, so they can be checked against later in many other contexts.

Yes, it's possible to use lazy-static and all, but having it all clean and const would be awesome.

We could skip the ArrayVec parts since they are quite limited

Yep, agreed.

aldanor avatar Nov 07 '21 12:11 aldanor