Best way to have an immutable global array/Vec of &str/String loaded from a file at compile-time?


#1

Hello,

the following situation: I’m writing a telegram bot using telebot. I will add commands using bot.new_cmd("/<cmd name>").and_then(handle_*);, where handle_* is a function in a separate module (as i plan to separate commands by category, using modules).

Some of those commands only have the purpose of selecting a random string from a list, formatting it and sending the result. I would like to define those string lists in separate files, one per line, and they will never change at runtime. (If i didn’t want to load them from files, i would probably not be asking this question, as i would just use a &'static [&'static str].)
include_str! Sounds promising and is probably what i would use to load the file content. (If i get it right, this reads the file at compile time and inserts the content - perfect.) But i need an array of the lines, and it should be const/static, so…
This is what i’ve found/considered so far:

  • lazy_static: Would definitely be an option, but i don’t need anything mutable, so i don’t know if it would fit.
  • Rethink my concept: This project is basically an attempt of rewriting a Telegram bot i’ve written in Java; there, i had every command as a class with one instance, so in theory, i could do the same thing here, by having structs in a map and hooking into the get_stream() method of my telebot::RcBot instead. That would make it easier and the commands more self-contained, but is it the, well, Rust-y thing to do? I’m doing this to learn and get better at the Rust way as well.

I know this became kind of complicated at the end, so here’s the question again:
Is there a working way of doing something like this
const TEXTS: &'static [&'static str] = include_str!("../texts/something.txt").split('\n').collect();
?

Thanks!


#2

AFAIK, Rust’s const evaluator is not yet powerful enough to do this as elegantly as you describe, but you can achieve a similar effect today by generating a file containing the TEXTS const using a build script.


#3

lazy-static only supports immutable stuff; you put a Mutex or something inside if you need mutability. This is what I’d do, personally.


#4

I don’t see a reason for loading them at runtime, if you have to define behaviour of the command in the source anyway. So why not just:

enum Command {
Cmd1,
Cmd2,
Cmd3(param1),
Cmd4(param1, param2)
};

then implement From<&str> for Result<Command, ParseError> to parse it.
Example: https://gist.github.com/rust-play/89e08e88e1907d5d18875efdeb4b3b4a


#5

@steveklabnik Thanks for the info! I’ve only found it used with Mutex, that’s why i thought it was mainly for mutable stuff. I think i’m gonna use it then!

@Fiedzia Thank you for the input! I will definitely consider that in the future, but i don’t see an advantage over using telebots new_cmd method right now (as i don’t need more control at the moment). An enum would also make an enourmes amount of sense for the command results tho, as they can be anything out of images, animated GIFs, audio, simple text…then again, i’m focusing on simplicity at the moment, and if every command calls bot.message().send() itself, i don’t even need any abstraction.

Thanks for all the input!