Rust rayon crate par_iter().map() "stalls" when saving images - rust

I'm trying to learn Rust from an example on data-parallelism in this linked rust-cookbook example.
However, when I run the code (with what I believe are the correct dependencies), the parallel iterator functions do not complete. This is the exact repo I am struggling with.
When I run cargo run at the root of the project containing 5 images, I get the following in the console.
Current number threads: 4
Saving 5 thumbnails into 'thumbnails'...
However, none of the thumbnails are created, and the program never exits. The code compiles.
I have the same problem in another little CLI I'm working on to try to learn Rust, but I think the example from the cookbook is easier to work with.
Any tips, even with what I might look for, would be greatly appreciated. I wonder if it is machine relating.

This is a known deadlock bug in the image crate's JPEG decoder: GitHub issue
The JPEG decoding itself makes use of rayon internally by default, and when you use rayon yourself to decode several JPEGs at once, this bug is triggered. As a workaround, you can stop the JPEG decoder from using rayon by specifying the image crate dependency in Cargo.toml like this:
image = { version = "0.24.1", default-features = false, features = ["jpeg"] }
That disables the image crate's default features, one of which is jpeg_rayon.

Related

Prevent access to dependencies in examples in Rust project?

I am making a library in rust, and I learned that I could put example usages of the library in an examples directory in the root directory and then run them with cargo run --example hello. However, I noticed that the dependencies I specify in Cargo.toml are also available in the example code. This is a bit odd to me. These dependencies belong to the library and the user shouldn't be aware of them and certainly not use them himself (unless he happens to depend on them too, I guess).
For example, I have nalgebra = "0.31.0" in Cargo.toml and use it as a backend for vectors and matrices and other mathematical operations.
I created my own wrappers for vectors and matrices, so the user shouldn't access nalgebra types directly. I find it weird that when writing examples for the library, I can write use nalgebra as na; and use it without any issues. I guess it's helpful for debugging purposes, but when writing a complete example, this code shouldn't compile.
Is there a way to generate compiler errors when trying to use dependencies in example code, or is it simply my responsibility to write examples that reflect actual usage of the library?
is it simply my responsibility to write examples that reflect actual usage of the library?
Yes.
This comes up in other situations too: as long as you're writing code inside a single Cargo package, all that code shares a common set of dependencies (with the exception that [dev-dependencies] are not available to lib and bin targets, only test, example, and bench targets).
If you want a different set of dependencies, the only option is to split the example(s) into a separate package (which can be in a workspace with the library package). In my experience, it's somewhat common for Rust projects to have a separate package for examples, particularly when those examples have shared code; for example, a graphics library whose examples need window-management code might have structure like
Cargo.toml # workspace declaration
the-library/
Cargo.toml
src/
lib.rs
examples/
Cargo.toml # has a `{ path = "../the-library" }` dependency
src/
lib.rs # contains setup code the examples use
examples/
ex1.rs
ex2.rs
There is a disadvantage to doing this: your examples won't be included in the package uploaded to the registry, and the new rustdoc feature that shows snippets of examples in the docs of functions that they use, won't find these examples.
However, if your only concern is “the examples shouldn't refer to this library”, then probably the best option is to take care writing the examples.
You can also use clippy to check for unwanted mentions of functions or types, with the disallowed_methods and disallowed_types lints. However, the lists of disallowed items are package-wide configuration, so you'd have to specifically disable the lint inside your library so that clippy doesn't warn on those uses of the dependency.

Can I test just the code in a single module?

I've got a Rust project that uses a fairly large framework. Compilation and macro expansion take a really long time. If I make a tiny change to the code, it takes a minute or more before before "cargo test" actually executes.
Is it possible to create a sub-project or sub-module within the same crate and test just the code in the module, assuming there are no dependencies on code outside the module?
You might be interested in "cargo workspaces" (https://doc.rust-lang.org/book/ch14-03-cargo-workspaces.html).
Essentially, instead of splitting your code into multiple mods, you split it into multiple crates. These crates can depend on each other via "path dependencies". For example, you could have something like:
[dependencies]
my_helper_crate = { path = "path/to/crate" }
The book has much more detail on this, but a nice feature of using workspaces is that your crates can have separate Cargo.tomls, but share a Cargo.lock, so you won't get issues around incompatible versions of crates.
With this setup, you can build one crate without building the rest of them, so you can cut down on a dev feedback loop.
However, if you have crate_a which depends on crate_b, building crate_a still requires building crate_b, there's not really any getting around that. The benefit is mainly for the leaves of your dependency graph.
Yeah, cargo test will take arguments which match specific tests that you want to run (Cargo book). For example, if you have modules foo and bar, you can run cargo test foo to run tests from that module, excluding all others.

Code works fine copy/pasted into my main.rs, but is ignored when run from its own external crate

tl;dr I'm trying to figure out why the avr-delay::delay function doesn't cause any delay when imported as an external crate, but when copy/pasting the code from avr-delay/src/lib.rs into my main.rs, it all works as expected and creates the 1000ms delays I was expecting. Source code for avr-delay here and my modified and working correctly avr-rust/blink code is here
I'm trying to learn Arduino the hard way, with Rust, and I'm doing a sort of Hello World by making an LED blink. I've gotten the light to blink at 1000ms intervals just as expected when I copy paste the contents of avr-delay's lib.rs into my main.rs, but when I import the avr-delay crate and use it that way, the delay is barely perceptible.
I've ensured I'm doing a 1:1 test by cloning the avr-delay repo and importing the local copy in my Cargo.toml, and I've tried hardcoding the CPU frequency on this line in my local copy of avr-delay just in case it was a problem with reading the env var that I'm setting or something.
I'm just really confused why the exact same code works in my main.rs but not in its own crate? Since this seems to be an issue with the build or Cargo.toml, here's how I'm actually building the bin. Could be related? I'm using the same command to build every time:
cargo +nightly-2021-01-07 build -Z build-std=core --target avr-atmega328p.json --release --verbose
Also worth mentioning that I'm playing with a knockoff Arduino, but it's still the same microcontroller (atmega328p) as an Arduino Uno, and since I can get the blinking to work sometimes it makes me think that's not the issue either.
Any help appreciated, I know this is a really specific question but I'm hoping there's something about the build or Cargo that I'm not understanding, since the code itself seems to be good.
The solution was to add #[inline(always)] to the functions in avr-delay, or add lto = true to my project's Cargo.toml. The delay code simply doesn't work when not inlined. I'm still not fully understanding why, but I'm guessing some of the low-level stuff didn't work when not inlined.

How to document a binary Rust crate project?

I have a program that I've written in Rust and I would like document it. I've looked on line and found:
https://doc.rust-lang.org/stable/rust-by-example/meta/doc.html
https://rust-lang.github.io/rfcs/1574-more-api-documentation-conventions.html#appendix-a-full-conventions-text
I've added doc comments to the code:
/// This is a documented function
fn example() {
println!("Hi there");
}
The problem I'm having now is that after I run cargo doc, I get all the other documentation generated for all the crates my program uses, but the HTML documentation does not induce my program. I've been looking around and I haven't see anything that talks about just documenting a single program, usually I see material about documenting crates. If I can use Rust's documentation system for a stand alone program, how would can I do this?
The problem is that your function is private (which makes sense for a binary crate). However, rustdoc currently only documents public items by default (which makes sense for a library crate). You can use the flag --document-private-items to also include private functions and the like:
cargo doc --document-private-items
There is some discussion about this feature and the default we should be using here.
Furthermore, I recently opened a PR to change the default behavior for binary crates, so that private item for binary crates are documented by default. This change is expected to arrive on the stable compiler on January the 31st 2020 (1.41.0).

Is it possible to specify version for feature in dependency in Cargo.toml?

For example, I use barcoders crate:
barcoders = {version = "0.10.0", features = ["image",]}
Is it possible to specify which version of image this dependency should use?
Something like
barcoders = {version = "0.10.0", features = ["image=0.22.3",]}
Because it uses image crate version 0.18.0 and in my project I use latest 0.22.3.
Does it mean that there's only 2 ways to resolve that:
I downgrade version in my package
Barcoders dependency get updated
No, there is no way to specify the version for a dependency's (optional) dependency. This makes sense, as your dependency run their tests only against the version they specify in their Cargo.toml. In this case, as it appears everything you're doing uses open source, you could fork barcoders, update the dependency, run the test suite and if it passes, use your fork. It would also be polite to open an issue in that case.
If barcoders wasn't open-source, so you couldn't fork it, your best bet would be to switch to the version of image that barcoders uses. If your crate is a library, it may be annoying to expose a public interface that uses outdated libraries, but that's life. The "proper" solution to this problem is to wait until image has a 1.0 release, which is basically a forward compatibility promise, then barcoders can specify image = "^1" (i.e. >=1.0.0 <2.0.0). I mention this "solution" only because you appear to have commit privileges on barcoders, in fact you solved your own problem by updating the image dependency in barcoders.
As one of the comments points out, this version compatibility issue is less fragile that it at first seems. So long as types from different versions of some dependency crate don't cross api boundaries, your project can include any number of versions of that dependency simultaneously. Working with multiple versions of libraries took some work from the rust team on name mangling, which you can read about here
No, you can't, and shouldn't, and shouldn't worry.
Libraries were developed at a single point in time, used dependencies with a certain API. The dependency is likely to change some of that between major versions (changing the type a function returns, exposing different patterns, or whatever). This may make it unable to compile anymore. To really update something, you might need to change parts of the code that is using the dependency in the first place.
This is open source world, so you can do so and publish a pull request in the original crate to update. It might be appreciated, but don't underestimate the care that needs to be taken to not break other people's crates yourself when doing so.
Or make your own fork of the crate that updates it just for you.
But you are probably just worried seeing duplicates of the same crate with different versions during compilation. Cargo indeed compiles with different versions, so all calls to the dependended crate will receive what the developer expected when he/she wrote it. This is not a problem, in performance, or amount of instructions that end up in the binary. Just stop worrying.

Resources