Can libraries be distributed as a binary, so the end user cannot see the source code?

A lot of metadata is included with each library crate, be it statically linked (.rlib) or dynamically linked (.so/.dylib/.dll):

  • module structure
  • exported macro_rules macros
  • type and trait definitions
  • constants with their initializer expressions
  • signatures for all functions
  • the entire body of each function that is marked as #[inline] or is generic (default trait methods are considered generic over Self)

All of this is enough to reproduce some of the original source (how much depends on the usage of generics), albeit with no comments or other whitespace.
The function bodies are serialized in the compiler’s internal AST structure – you can see a pretty form of it with rustc -Z ast-json lib.rs.

While the metadata is binary, not JSON, using librustc to extract all exported function definitions from a compiled crate, and pretty-printing the ASTs is fairly easy.

In the future, there might not be any AST past type-checking, so the metadata would encode an IR of sorts – one possibility is CFG, i.e. “control flow graph”, which is already used internally in a couple places.

However, that would still expose more information than Java bytecode, it would be an optimization, you could still approximate the original code (and easily get something which compiles).

As such, there are only two options I can recommend:

  1. expose a C API; it has the advantage of being a stable ABI, but it’s quite limiting and brittle;
  2. expose a Rust API using only trait objects, instead of generics; this way you get to keep memory safety and all monomorphic functions would still work normally, but trait objects (dynamic dispatch) cannot express all the patterns possible with generics: in particular, generic trait methods are not callable on trait objects (C++ should have a similar restriction for mixing template and virtual, with workarounds potentially available on a case-by-case basis).

Leave a Comment