Is cargo intended to act this way, or it a bug?

I'm experiencing some confusing behaviour from cargo involving a particular crate, and I'm unsure whether it's something that:

  • I should report to the crate maintainer
  • I should report as a bug in cargo
  • I should report as a bug to crates.io
  • is some combination of the above

I'll now layout some background, then describe the confusing behaviour.

First, the crate I'm trying to use is raylib-rs, which is a Rust binding to Raylib, which is a library for making games, written in C. I've used this crate before, to write small hobby games/game protoypes, etc. and I even made a template to make starting new projects with the library quicker. This prior usage has been with version 3.5 of the crate, which corresponds to version 3.5 of the C library.

Version 3.7 of Raylib, the C library, has been released, and correspondingly, version 3.7.0 of raylib-rs has been released. Some parts of the API have been added, at least one small part removed, and other parts have been renamed between the two versions. Because the weird behaviour is related to version selection, I'll mention that I don't believe the version numbering strictly follows Semantic Versioning as described at semver.org. The confusing part happened after I started trying to update my template project to use 3.7.0.

Specifically, I am able to repeatedly perform the following steps and end up with a crate in a confusing state:

  1. Copy commit e5e5bbe of my template to a new folder, (note, without a target directory). I am able to successfully run the program with cargo r, (which of course creates a target directory). When running it I see Compiling raylib-sys v3.5.0 in the terminal output first.
  2. Change the specific Cargo.toml file that specifies the raylib-rs dependency. Specifically, change the line raylib = { version = "3.5" } to raylib = { version = "3.7" }. After running cargo r we see Compiling raylib-sys v3.7.0 in the output, and it fails to compile because the API has changed. Also, the Cargo.lock file has been updated. All expected behaviour so far, I think.
  3. Change the line in that Cargo.toml back from raylib = { version = "3.7" } to raylib = { version = "3.5" }. Then run cargo r again. I see the same compile errors as the previous time.
  4. Since the Cargo.lock had been previously updated, and it contains references to 3.7.0, I try removing it, and then running cargo r again. I again see Compiling raylib-sys v3.7.0 in the output, and I still see the same compile errors.
  5. If I run git restore Cargo.lock then when I run cargo r then the executable which was previously built with 3.5.0 runs. I can then run cargo clean, and then build a fresh executable with cargo r. Normal behaviour has been restored.

I'm unable to reproduce it but I also somehow once got it to start compiling raylib 3.7.0, while that was happening, then the executable built with 3.5.0 started up, then after I closed it, the 3.7.0 crate continued compiling, and eventually ended in the same errors.

As I said at the top, I'm not sure where the best place(s) to report a bug for this is. My best guess is to raise an issue on the cargo repo, but it seemed best to ask here first.

This may be irrelevant, but this is the same machine where I ran into a strange ICE which I reported to the cargo repo. I tried reinstalling stable, as suggested in that issue, after I ran into this issue fir the first time, but that didn't seem to do much in this case.

Edit: Although this post as written seems to point to just expected behaviour, I have described an additional step below that I currently believe indicates an actual bug.

1 Like

I think it's normal (due to assuming semver). What does cargo update --dry-run say when you're starting from step 1 (or 5)?

1 Like

The crate doesn't follow semver, but cargo tries to. When you switch from 3.7 to 3.5 without removing the lock file, cargo considers 3.7 (in the lock file) to be a superset of 3.5, so it uses what's already there.

1 Like

The problem is that Cargo packages MUST follow the Cargo flavored semver. It's a bug from the raylib-rs crate if it breaks compatibility during the update from 3.5 to 3.7.

As a workaround, you can specify the version of the dependency using the tilde requirements to pin the minor version.

3 Likes
    Updating crates.io index
    Updating cc v1.0.71 -> v1.0.72
      Adding cfg-if v1.0.0
    Updating cmake v0.1.46 -> v0.1.48
    Updating libc v0.2.104 -> v0.2.112
    Updating raylib v3.5.0 -> v3.7.0
    Updating raylib-sys v3.5.0 -> v3.7.0
warning: not updating lockfile due to dry run

I see that this output fits with the notion that this is just the crate not following semver.

Starting after Step 2 above, I changed the version from "3.7" to "~3.5.0". I then get a different set of compile errors. This time they originate in the crate's code in my .cargo folder.

Here's one of the errors:

error[E0560]: struct `raylib_sys::NPatchInfo` has no field named `type_`
  --> /home/ryan/.cargo/registry/src/github.com-1ecc6299db9ec823/raylib-3.5.0/src/core/texture.rs:43:13
   |
43 |             type_: (self.type_ as u32) as i32,
   |             ^^^^^ `raylib_sys::NPatchInfo` does not have this field
   |
   = note: available fields are: `source`, `left`, `top`, `right`, `bottom`, `layout`

It seems to me that this case is still some sort of bug.

I had tried some different syntaxes for setting the version before, too, and I saw these same errors at that time, but I forgot to mention them in the first post. I can see now that leaving that part out gave the wrong impression.

FWIW cargo update --dry-run only outputs the following in this state with the library errors:

    Updating crates.io index
warning: not updating lockfile due to dry run

Going from a point where it compiles, (step 1 or 5) and then changing it to "~3.5.0" results in the following output from cargo update --dry-run:

    Updating crates.io index
    Updating cc v1.0.71 -> v1.0.72
    Updating cmake v0.1.46 -> v0.1.48
    Updating libc v0.2.104 -> v0.2.112
    Updating raylib-sys v3.5.0 -> v3.7.0
warning: not updating lockfile due to dry run

But if I run cargo r the program builds and runs.

So, in summary, I think pinning the version makes sense, and I am now less confused. But I think the state where the compile errors come from the library is still a state that should not be reachable. Does that statement seem correct, or have I misunderstood/forgotten something else?

Looking at this output, it seems like you correctly pinned the raylib dependency to v3.5.0, but raylib-sys is still free to update. Then, when you try to compile raylib v3.5.0 it tries to use raylib-sys v3.7.0 and chokes on the missing fields.

You should be able to add raylib-sys = "~3.5.0" to your Cargo.toml to make sure it gets pinned as well.

7 Likes

That works! I'm able to add the raylib-sys version and now it compiles and runs. If I change both, then I'm able to switch back and forth reliably now. Thanks!

Looking closer at the link @Hyeonu provided, I was able to figure out that "= 3.5.0" also works. I prefer that syntax since ~ next to numbers reads as "approximate" to me, which is the opposite of what the ~ syntax actually means in this case.

So this all really was arguably expected behaviour, since cargo was assuming semver was being followed. If I'm not mistaken, it would be useful for the raylib-rs maintainer to pin the dependency from the raylib crate to the raylib-sys crate going forward. As I understand it, that would prevent the case where compile errors come from the .cargo folder. I think I'll open a PR there, linking to this thread.

2 Likes