Rust development on nixos

Hello everyone, I’m new to nixos and I’m not sure what the best practices are for rust development. I currently use a shell.nix with the rust-overly provided by oxalica on github. I would however love to take full advantage of the nixos ecosystem so I did some research.

On YouTube videos I see something called carnix being used but when going through the github repo for it I can see it is depreciated with no links to a replacement or new repo, If there are any tools that you guys know of and have used recently please let me know. I’m not asking to explain in great details as I will be more than glad to research on any topics mentioned. I’m just not sure what the best practices are. Thank you in advance.

1 Like

I don’t claim any authority on what counts as best practices, but you should definitely take a look at crate2nix. It’s in nixpkgs/pkgs/development/tools/rust/crate2nix. You give it a Cargo.toml and it generates an overlay-friendly nix expression.

Nixpkgs currently can’t use nix-code-generators due to policy restrictions, so it uses rustPlatform.buildRustPackage.

I think crate2nix is the philosophical successor to carnix, but I could be wrong about that. All the rust-for-nix tools are named c[a-z2]*nix so it is easy to get them mixed up.

1 Like

I’d also recommend looking at GitHub - nix-community/dream2nix: Simplified nix packaging for various programming language ecosystems [maintainer=@DavHau] if you’re going down a 2nix route. It’d be nice if that could really become the standard, so you don’t need to go hunting language-specific tools anymore.

I’ve personally been using GitHub - nix-community/naersk: Build Rust projects in Nix - no configuration, no code generation, no IFD, sandbox friendly. [maintainer=@AxelSilverdew], which does the whole rust conversion without generating intermediate nix expressions. This is nice, because you can hook it up to a shell and not run into situations where you forget to update your nix expression after you update your Cargo.toml.

dream2nix does this similarly (for languages where it’s possible), I believe, but it came into existence after I settled on naersk for most of my projects.

Stuff like crate2nix and carnix is better for packaging than development (which is why it lives in nixpkgs proper, conforms to policy and is relatively simple to use), where having to update your nix expressions is not a significant overhead. I absolutely loathe dealing with it during development, though.

2 Likes

There is also the new crane Nix lib for working with cargo projects

It works similarly to naersk, by using Cargo.toml & Cargo.lock files directly and making very configurable derivations from it.

As you can see, there are many options here. It can be difficult to decide! The wiki has a nice comparison table.

Most of these tools end up calling one of two different low-level primitives inside nixpkgs, which have unfortunately-not-very-descriptive names:

  • buildRustPackage is for expressions which have cargo drive the build.

    • a Rust binary package will build as one gigantic nix derivation which calls cargo on a tarball which vendors all the source code of all the crate’s dependencies.
    • There is no sharing of build effort between binary crates that share a common library dependency (i.e. the library is built two or more times).
    • Builds are “monolithic” – if you change your source code or any of your crate’s dependencies (including adding a dependency), all of your crate’s dependencies are rebuilt.
    • It is currently impossible to override a crate’s dependencies without running cargo update. This is a problem because cargo update accesses the network and cannot be run from inside a nix derivation. So if you want to override some pervasive dependency (like the ring cryptography library) in every rust package on your system you’re looking at doing a whole lot of work by hand, and then redoing that work every every time you pull from nixpkgs.
      • Once this PR merges it will be possible to do non-manual mass-overrides, but still awkward.
  • buildRustCrate is for expressions which have nix drive the build.

    • Each Rust library (.rlib) gets its own derivation, in which nix calls rustc directly.
    • Builds are incremental – only the libraries which change are rebuilt.
    • Overriding dependencies is relatively easy; it works just like we’re used to with .override since the whole build plan is tree of nix derivations.

It looks like dream2nix uses buildRustPackage.

I looked briefly at naersk and crane; it looks like each of them rolls its own equivalent of buildRustPackage (i.e. they call cargo and let it drive the build). So if you plan on submitting to nixpkgs at some point you would need to change tooling…

It looks like in both cases they are a tiny bit smarter than buildRustPackage, creating two derivations instead of one: one monolithic derivation for all of your crate’s dependencies and a second one for your crate’s source code. So changing your crate’s source code will not trigger a world-rebuild. But changing or adding any of your dependencies will still rebuild all of your dependencies, and cargo is in still driving the build.

2 Likes

Thank you so much for all the replies @amjoseph @TLATER and @bew. I’ll be testing all of the solutions out for the next month to get an idea of what will be best going forward. You guy’s kind need have made me feel very comfortable with the nixos community! I appreciate it a lot :grin:

2 Likes