Tools Team: tell us your sorrows

Lots of people looking at debugging and thinking GDB or LLDB. That’s not really enough, as far as I’m concerned - step-into, step-over, run-till-end-of-function, breakpoint and breakpoint counter are useful, but it also needs a full-featured REPL for exploration. Ruby’s pry-byebug + irb is the bare minimum required feature set here; drop a single keyword in at any point in the code and get a REPL with full information about the current scope where you can execute and inspect essentially anything, change things, set variables, declare entire new variables, data structures and methods if you feel the need, and then let it get back to running. This is doubly vital because of Rust’s compile times; changing a few lines and recompiling takes seconds or minutes, so the ability to try things within a scope and get instant (or at least imperceptibly fast, say < 100ms) feedback would be extremely valuable.

IDE integration not necessary, really; command line alone would be sufficient for most use cases.

In my Rust 2019 blog post I wrote about a particular problem in cargo which particularly impacts no_std developers, where bad interactions in the cargo feature dependency calculation involving build-dependencies and dev-dependencies can inadvertently “flip on” the std cargo feature in dependencies of the target:

Some relevant open issues:

It seems like all my heavily bolded text got the core team’s attention and @alexcrichton had suggested a potential fix to the way Cargo does the dependency calculation.

4 Likes

A small tipp that could help your workshops:

Have you investigated using https://github.com/mozilla/sccache? It’s a build artifact cache that supports Rust and has distributed storage (S3, Redis, …).

You could have Redis running on a presenter notebook. The participants would have to install the sccache binary (binary releases are available) and configure the remote host, but this could cut down the build times very significantly if you have the cache populated.


That said, it would be awesome if cargo had this functionality built in: a) sharing crate builds between projects b) distributing builds for the architecture via crates.io

Area: RLS and running cargo commands

(I’m the author of cargo-watch.)

Increasingly I am finding myself running an RLS via my IDE, and a cargo watch incantation. Both are useful: the RLS provides IDE services like type discovery, linking, in-context errors/lints; the watch incantation can run other things (test suite…) but also presents results differently (all check results in one place, particularly, which is useful in various ways e.g. when an issue is the result of something spread out over many files, or as a way to see the scale of breakage a change introduced, or even because of UI dislike/preference).

My issue is in the duplicated effort when both the RLS and cargo check run. That hurts in two ways: firstly it’s wasteful and uses twice as much CPU (and battery, etc) as really needed. Secondly the RLS has the tendency to run first and take a lock, blocking other cargo processes until it’s done — if the RLS runs several analyses, it and manual cargo contend for time and the entire thing slows down to unusable.

Ideally it should be possible to have a tool that discovers and interrogates a running RLS for the latest results (I’m aware that’s not really how the LS protocol works, though), then displays them in the same or similar format as cargo check. Or some other way to share the results between both approaches.

1 Like

Have you tried IntelliJ’s CLion? Some co-workers of mine were using it for Rust dev and it seemed very good. Intellisense, debugging, etc.

1 Like

Yes, i’m using it personally. It’s usable, but still less than satisfying.

First it only works with -gnu flavor and doesn’t support msvc flavor on a windows box. And it only integrate with gdb debugging and not msvc-flavor debugging. And there’re quite a few annoyance with debugging experience(many included in the comment i posted above), significantly worse than MSVC C/C++ debugging experience.

The overall feeling is that: at its current status, i can use its debugging facility if i have to, but i’ll avoid it if i can. And no, i’m not advertising it to my C++ programmer colleagues (most are currently visual studio users) this time. It’s good in some ways, but i feel it’s not good enough (yet).

  • GDB is unusable on Mac without patching it (SIGSEGV that’s not Rust’s fault), but even if you do nothing pretty prints because the rust-gdb wrapper doesn’t work.
  • LLDB “works” but it has no way to distinguish between mod.rs files that it’s not fun to use. Even then I find LLDB to just not be a usable debugger to work with
  • RLS/Racer/Any tool that uses rustc to check things is unusable. My work codebase is 200K+ lines before macro expansion. It adds so much latency that my editor grinds to a halt. Rust analyzer so far is alright, but still some false positives and it’s just young

No I do not want to use CLion, my work flow is fairly integrated into my current editor, I’m not going to switch for one lang. What I’ve learned in the past 7 months is we optimized for the hobbyist in tooling. It was good enough, but we have nothing for production use cases basically :confused:

I get why this is the current state of things. I’m not angry at people, especially since it’s being worked on in people’s spare time. I’m just frustrated by the lack of usable tools. I spend a lot of time compiling just to print stuff out. If we can reduce that cycle even a bit it would be amazing.

6 Likes

Racer does not use rustc. Order of magnitude of my code base is similar to your, so RLS is also unusable for me, that's why I use only racer. In future I hope to find some time to look at GitHub - rust-lang/rust-analyzer: A Rust compiler front-end for IDEs , it should be more accurate then racer with comparable speed with racer.

Installing rust-doc on Windows is very slow. This makes for a less than ideal experience on Windows, especially if you track nightly.

Windows CI is also greatly affected. Checkout the difference in time to build "Hello, World" on Travis for Windows:

10 Likes

This one is a pretty critical issue if the tools team has any kind of prioritization.

6 Likes

Area: Cargo / Cargo.toml – checking for unused dependencies

Sometimes there are dependencies that may be in Cargo.toml that do not have to be there, such as when a crate was previously used, and is no longer used, but only the use statements have been removed from .rs files, and it still remains in Cargo.toml. Haven’t verified, but it may result in unnecessary compilation of those dependencies.

In addition, there are dependencies that may be in the [dependencies] section when they could reside in [dev-dependencies] (e.g. only used by code in #[cfg(test)]). Meaning they are unnecessarily compiled for library consumers. This situation inadvertently happens when a crate initially uses a library, then no longer depends on it, but has tests that check that both the crate and the library work together.

  • Is there a tool for this already?

    No

    • If not: did you write one?

      Attempted to use rustc-ap-syntax to try and do this, but haven’t had enough time to dedicate to it. Can see how it’s possible though – have got a hacky program that traverses a rust file’s syntax tree. Would need to build the logic to:

      • traverse the whole crate’s source files
      • group item usages – always, used with #[cfg(test)], used with #[cfg(feature = "something")]
      • intersect these with info from Cargo.toml (dependencies, dependencies (optional = true), dev-dependencies, feature)
      • output in a human understandable format
    • If not: which tool do you think it should be included in?

      Perhaps cargo itself, or clippy.

  • Did you manage to work around the lack of tooling?

    No not yet.

13 Likes

No. Editions switch some syntax options in otherwise compatible language, while this is about features added over time. Adding std or core as explicit dependencies seems like a good option here as that is what it basically is, taking the compiler with it as rustc and std versions are always the same. The version of std (or core) should also be written to Cargo.lock. And if current version of cargo chokes on it, it is the right thing in this case.

Problem: There’s no way to make examples and tests have different dependencies, so you can’t make a set of cross-compiled example programs if you also want to have any sort of quickcheck / fuzzing / etc because those all use std.

Even their no_std mode does not compile on my target device because they still use atomics and stuff that my target device doesn’t have (GBA). So I’m just stuck not having the fancy test crates at all.

10 Likes

Area: Testing

Better tooling around testing that code does/doesn’t compile would be super helpful when writing safe wrappers around unsafe code. The current solution is compiletest, however I’ve found compiletest to be very difficult to use. The biggest pain points I’ve run into:

  • Documentation is lacking. The last time I tried to use it, I gave up because I couldn’t figure out the syntax for specifying the expected compiler error.
  • It doesn’t integrate cleanly into the existing cargo test functionality. If I write a dozen compile tests, they all get lumped together under a single test report, making the test output difficult to read.
  • Making contributions requires making the changes upstream in the main Rust repo, and then waiting for those changes to be pulled into the standalone tool.
  • There a handful of unnecessary test modes that don’t make as much sense in the standalone tool as they do for the version used in the main Rust project (e.g. the run-pass and compile-pass modes).

While compiletest is a decent tool, it would greatly improve things if cargo test directly supported compile tests, and provides a more streamlined experience for writing such tests.

4 Likes

Area: Testing, Code Generation

Currently it’s kind of painful to write tests for a crate that does code generation. In such a case, you need to test three different things:

  • That your code generation logic runs correctly.
  • That the generated code compiles.
  • That the generated code run correctly.

The first point can generally be tested using Cargo’s test functionality, but in most cases you need to create a second crate in order to write test cases for the other two. While this isn’t a terrible workaround, it makes the development experience for code generation crates somewhat painful, as you often have to jump between the two crates in order to fully test changes (though this can in turn be worked around somewhat when using a Cargo workspace). This situation could likely be improved by giving cargo test more direct support for running compile tests.

2 Likes

Sorrow: Cargo features don’t let you set other parts of the Cargo.toml, such as crate-type, codegen-units, or lto

UPDATE: yeah I finally broke cargo and now I just have to have two different Cargo.toml files, one for development with dev features and one for running the tests with no features on.

Area: compiler diagnostics

Rust produces very verbose compile errors that generally explain exactly what when wrong and how to go about fixing it so only a small number of error messages can fit in a terminal window at once. When refactoring code, I’ll sometimes get dozens or even hundreds of the same error message pointing to all the places in my code that need to be changed. It would be nice if there was a cargo build --oneline option that condensed each error to only a single line so that I didn’t need to scroll through 20 pages of output to fix them all.

This is already possible, try cargo build --message-format=short.

8 Likes

sorrow: cargo feature and dependency resolution approaches are broken by design. All features of all targets must unify, which means that you can’t have different dependency graphs for testing, building, benchmarking, etc. resulting in issues like the one @Lokathor mentioned above, but even worse since this affects also target-dependent dependencies.

sorrow: cargo does not detect cyclic dependencies properly when dealing with dev-dependencies, preventing some crates from being released to crates.io. E.g. crate_foo.tests require bar which requires crates_foo is detected as a cyclic dependency, even though there is no cycle (e.g. crate_foo.tests always requires crates_foo, which works just fine).

14 Likes

CI on windows takes ages. One of the reason is:

It makes no sense to install documentation during CI build, but on CI/Windows rust install 3x times slower then on Mac OS X / Ubuntu Linux, and this is mainly because of rust-docs component installation.

7 Likes