Format! like functionality in const fn to generate a &'static str

Hey :slight_smile:
I try to generate a lookup table which consists of &'static str.
This lookup table is const and can in theory be calculated at compile time.
My issue right now is, that I would like to use a variable from the const fn to be part of the &'static str i write into each array element.

const LOOKUP: [&'static str; 8] = {
    let mut tbl = [""; 8];
    let mut i = 0usize;
    while i < tbl.len() {
        tbl[i] = "0xi"; // big question is how to solve this here
        i += 1;
    }
    tbl
};

pub fn main(){
    println!("{:?}", LOOKUP);
    // what i would want to read is
    // ["0x0", "0x1", "0x2", "0x3", "0x4", "0x5", "0x6", "0x7"]
    // right now it only reads 0xi for each element
}

I know that there is the const_format crate for exmaple, but it is denying work, because i is not const when calling it's formatting macro. Any ideas how to solve this?
Is it possible to do what I want in Rust right now?

1 Like

You can do this with a proc-macro. Here's an example that doesn't need any extra dependencies (though you might eventually want to use quote and proc-macro2):

// strmac/lib.rs
use proc_macro::TokenStream;
use std::fmt::Write;

#[proc_macro]
pub fn make_lookup(_item: TokenStream) -> TokenStream {
    let mut lookup = String::from("[");
    for i in 0..8 {
        lookup.write_fmt(format_args!(r#""0x{i}","#)).unwrap();
    }
    lookup.push(']');

    lookup.parse().unwrap()
}

Use it in your other crate by importing the macro:

use strmac::make_lookup;

const LOOKUP: [&str; 8] = make_lookup!();

fn main() {
    println!("{:?}", LOOKUP);
}

Alternatively, if you don't plan on having proc macros and don't mind extra deps, there's the seq-macro crate:

#!/usr/bin/env rust-script
//! ```cargo
//! [dependencies]
//! const_format = "0.2"
//! seq-macro = "0.3"
//! ```
//!
//!
use seq_macro::seq;
use const_format::concatcp;

const LIST: [&str; 8] = {
    let mut l = [""; 8];
    seq!(i in 0..8u64 {
        l[i as usize] = concatcp!("0x",i);
    });
    l
};

fn main() {
    println!("{LIST:?}");
}
// ["0x0", "0x1", "0x2", "0x3", "0x4", "0x5", "0x6", "0x7"]

it's a bit finnicky though with what it accepts in the ranges though...

I really would prefer to not include any other dependencies (I always think the meme about node.js modules and black holes), but this really looks tidy and compact <3

Too bad this somehow is so cumbersome with just pure std Rust :confused:
Would prefer if the language would be a tiny bit more expressive/flexible, in order to write concise code, instead of having to write half a book or add extra dependencies.

Thanks for this, will play around with it after work :slight_smile:

Avoiding extra dependencies is actually something I would value a lot.
Too bad it isn't doable with pure std Rust, but a proc-macro is something I thought of myself as well.
Guess it is time for me to dive into that rabbit hole as well :smiley:
Thanks for taking the time to formulate this snippet, will play with it after work.

You can write bytes to buffer and then call std::str::from_utf8.

Didn't know this existed, it certainly will come in handy at one point, thanks!
If I feel like it, I might write my own little macro for formatting in const functions with it :smiley:

That crate is cool konst - Rust

See also How to format a const str using const generic? - #11 by RustyYato