Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deserialize file using serde_json at compile time

Tags:

At the beginning of my program, I read data from a file:

let file = std::fs::File::open("data/games.json").unwrap();
let data: Games = serde_json::from_reader(file).unwrap();

I would like to know how it would be possible to do this at compile time for the following reasons:

  1. Performance: no need to deserialize at runtime
  2. Portability: the program can be run on any machine without the need to have the json file containing the data with it.

I might also be useful to mention that, the data can be read only which means the solution can store it as static.

like image 647
Nils André Avatar asked Oct 12 '19 23:10

Nils André


1 Answers

This is straightforward, but leads to some potential issues. First, we need to deal with something: do we want to load the tree of objects from a file, or parse that at runtime?

99% of the time, parsing on boot into a static ref is enough for people, so I'm going to give you that solution; I will point you to the "other" version at the end, but that requires a lot more work and is domain-specific.

The macro (because it has to be a macro) you are looking for to be able to include a file at compile-time is in the standard library: std::include_str!. As the name suggests, it takes your file at compile-time and generates a &'static str from it for you to use. You are then free to do whatever you like with it (such as parsing it).

From there, it is a simple matter to then use lazy_static! to generate a static ref to our JSON Value (or whatever it may be that you decide to go for) for every part of the program to use. In your case, for instance, it could look like this:

const GAME_JSON: &str = include_str!("my/file.json");

#[derive(Serialize, Deserialize, Debug)]
struct Game {
    name: String,
}

lazy_static! {
    static ref GAMES: Vec<Game> = serde_json::from_str(&GAME_JSON).unwrap();
}

You need to be aware of two things when doing this:

  1. This will massively bloat your file size, as the &str isn't compressed in any way. Consider gzip
  2. You'll need to worry about the usual concerns around multiple, threaded access to the same static ref, but since it isn't mutable you only really need to worry about a portion of it

The other way requires dynamically generating your objects at compile-time using a procedural macro. As stated, I wouldn't recommend it unless you really have a really expensive startup cost when parsing that JSON; most people will not, and the last time I had this was when dealing with deeply-nested multi-GB JSON files.

The crates you want to look out for are proc_macro2 and syn for the code generation; the rest is very similar to how you would write a normal method.

like image 72
Sébastien Renauld Avatar answered Oct 12 '22 23:10

Sébastien Renauld