Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there any way to include binary or text files in a Rust library?

Tags:

I am trying to create a library and I want to include some binary (or text) files in it that will have data which will be parsed at runtime.

My intention is to have control over these files, update them constantly and change the version of the library in each update.

Is this possible via cargo? If so, how can I access these files from my library?

A workaround I thought of is to include some .rs files with structs and/or constants like &str which will store the data but I find it kind of ugly.

EDIT:

I have changed the accepted answer to the one that fits more my case, however take a look at Shepmaster's answer as this can be more suitable in your case.

like image 218
Otobo Avatar asked Sep 23 '15 20:09

Otobo


People also ask

How do you add a library in Rust?

To include code of such external libraries, you have to include it as an extern crate. And to make your life easier, you really want to use Cargo! Now copy all the contents from include_me.rs into lib.rs (it is just convention to call the root file of a library project lib.rs ).

What is a binary crate?

A binary crate is an executable project that has a main() method. A library crate is a group of components that can be reused in other projects.

How is a binary file stored?

A binary file is one that does not contain text. It is used to store data in the form of bytes, which are typically interpreted as something other than textual characters. These files usually contain instructions in their headers to determine how to read the data stored in them.


2 Answers

Disclaimer: I mentioned it in a comment, but let me re-iterate here, as it gives me more space to elaborate.

As Shepmaster said, it is possible to include text or binary verbatim in a Rust library/executable using the include_bytes! and include_str! macros.

In your case, however, I would avoid it. By deferring the parsing of the content to run-time:

  • you allow building a flawed artifact.
  • you incur (more) run-time overhead (parsing time).
  • you incur (more) space overhead (parsing code).

Rust acknowledges this issue, and offers multiple mechanisms for code generation destined to overcome those limitations:

  • macros: if the logic can be encoded into a macro, then it can be included in a source file directly
  • plugins: powered up macros, which can encode any arbitrary logic and generate elaborate code (see regex!)
  • build.rs: an independent "Rust script" running ahead of the compilation proper whose role is to generate .rs files

In your case, the build.rs script sounds like a good fit:

  • by moving the parsing code there, you deliver a lighter artifact
  • by parsing ahead of time, you deliver a faster artifact
  • by parsing ahead of time, you deliver a correct artifact

The result of your parsing can be encoded in different ways, from functions to statics (possibly lazy_static!), as build.rs can generate any valid Rust code.

You can see how to use build.rs in the Cargo Documentation; you'll find there how to integrate it with Cargo and how to create files (and more).

like image 67
Matthieu M. Avatar answered Oct 09 '22 19:10

Matthieu M.


The include_bytes! macro seems close to what you want. It only gives you a reference to a byte array though, so you'd have to do any parsing starting from that:

static HOST_FILE: &'static [u8] = include_bytes!("/etc/hosts");

fn main() {
    let host_str = std::str::from_utf8(HOST_FILE).unwrap();

    println!("Hosts are:\n{}", &host_str[..42]);
}

If you have UTF-8 content, you can use include_str!, as pointed out by Benjamin Lindley:

static HOST_FILE: &'static str = include_str!("/etc/hosts");

fn main() {
    println!("Hosts are:\n{}", &HOST_FILE[..42]);
}
like image 44
Shepmaster Avatar answered Oct 09 '22 19:10

Shepmaster