For those who don’t know, rust-analyzer is the main rust LSP (Language Server Protocol). So super handy! Here’s their homepage: https://rust-analyzer.github.io/

I wanted to have it running for little all the programs and tests I’d write and run while going through “The Book” and have settled on a setup I thought I’d share.

The main problem I encountered is that having R-A run on a single file isn’t really a thing. It seems it kinda is but I couldn’t get it working and it seemed to be a pain anyway. Plus I wanted to be able to use both cargo projects/crates and little test programs/functions simultaneously from a single workspace in my text editor dedicated to working through The Book.

My solution

at the moment

What I settled on was:

  • Running multiple cargo projects under a single instance of rust-analyzer running in a single workspace in my editor
  • Organising simple test programs as specific functions in a single cargo project.
    • So far, I’m thinking of organising these thematically specifical cargo projects. So I’ve got one called “borrow_checker” for all borrow checker and ownership toy examples.
    • These specific functions can get called from a single main() if helpful/necessary.

EG directory structure:

the_book/
 ├──  Cargo.lock
 ├──  Cargo.toml
 ├──  guessing_game/
 │  ├──  .gitignore
 │  ├──  Cargo.lock
 │  ├──  Cargo.toml
 │  ├──  src/
 │  └──  target/
 ├──  hello_cargo/
 │  ├──  .gitignore
 │  ├──  Cargo.lock
 │  ├──  Cargo.toml
 │  ├──  src/
 │  └──  target/
 ├──  misc_play/
 │  └──  borrow_checker/
 │     ├──  .gitignore
 │     ├──  Cargo.lock
 │     ├──  Cargo.toml
 │     ├──  src/
 │     └──  target/
 └──  target/
    ├──  .rustc_info.json
    ├──  CACHEDIR.TAG
    └──  debug/

Basically everything inside the_book/ is its own cargo project.

And inside misc_play/borrow_checker/src/ is a typical main.rs with multiple functions testing or toying with something I was trying to understand … and hopefully use the LSP to help me with.

Getting this to work

So that’s the structure. But getting it to work requires a little trick. It requires using a Cargo Workspace.

In this case, it’s a matter of adding a Cargo.toml file at the top level (ie, the_book/Cargo.toml) with the following:

[workspace]

resolver = "2"
edition = "2021"

members = [
	"guessing_game",
	"hello_cargo",
	"misc_play/borrow_checker"
]

Here, members is clearly listing each of the projects or libraries. And I added the resolver and edition keys to silence a cargo warning about incompatible resolvers (which I don’t know anything about).

With this file, when at the top level, one can run cargo build to build all of the projects/packages. Or, cargo build -p SPECIFIC_MEMBER to build only a specific project/package. Meanwhile, rust-analyzer seems happy to work on any file from any of these projects in a single editor workspace.

There’s a section in The Book on cargo workspaces: https://doc.rust-lang.org/book/ch14-03-cargo-workspaces.html (where this feature is about much more than just getting rust-analyzer to work with my preferred directory structure!)


Anyone else have thoughts on how best to structure your code and setup for working through something like The Book or any other materials for that matter?

  • Jayjader@jlai.luM
    link
    fedilink
    English
    arrow-up
    5
    ·
    7 months ago

    Workspaces are great!

    Haven’t tried using them with The Book, but I did for 2023’s Advent of Code to easily chop up my code & compilation : https://github.com/Jayjader/advent-of-code-2023-rust/ . The relevant bits are in the Cargo.tomls, as well as the runner directory/crate: each day is its own compilation unit, effectively acting as libraries that are used by the runner binary. When I change a line of code in either the solution for a single day or the runner itself, only that directory gets re-compiled. Additionally, any external crates I use (notably itertools) are explicitly shared across the crates/days that use them - saving me disk space and compilation time. My crates are libraries (made using cargo new --lib) so I’m always invoking the same root cargo project to run things. If your sub-crates are made with cargo new [--bin], you “automatically” get the same behavior as cargo run --bin <your sub-crate name here>.

    It’s really up to what code ends up needing to be shared, and how it needs to be used/called, that determines whether to split it off as libraries or (sub-)binaries.

    • maegul (he/they)OPM
      link
      fedilink
      English
      arrow-up
      1
      ·
      7 months ago

      Thanks for sharing that, I’ll definitely look it up when I’m trying sorting similar.