Features for dependencies of dependencies

If you have a dependency (ie: graphql-async) and you are compiling for a wasm target, you get the following error

error: the wasm32-unknown-unknown target is not supported by default, you may need to enable the "js" feature. For more information see: https://docs.rs/getrandom/#webassembly-support
   --> /home/xxxxx/.cargo/registry/src/github.com-1ecc6299db9ec823/getrandom-0.2.3/src/lib.rs:219:9

graphql-async supports wasm as a compilation target; and so does getrandom but only if you set the js feature. graphql-async does not have a wasm feature to enable this kind of targeting and it might actually make sense (why would it if it compiles to these targets with no strings attached).

So If I do this in my Cargo.toml file

getrandom = { version = "*", features = ["js"] }

... my program will compile. This is not really a kind of predictable behavior. It seems to be rather a bug (for me) and not a feature. This is basically Cargo picking a single version (with a specific features set) of getrandom because I'm using the wildcard.

Why this is a problem?

This could be a problem if I am using a package with a certain feature set; and a certain dependency is using the same package/version but is not compatible with that particular feature set.

How to fix it?

I'm not really that versed in this particular topic. But I think it would be better if I could select the feature set for the dependencies of a dependency. Another idea, is to make it possible to propagate features through the crates. For example, async-graphql could have a "js" feature that select the "js" feature of getrandom.

AFAIK, that's already possible, though it would need all the crates in the hierarchy down to getrandom to support such flags.


The docs from getrandom suggest another approach

getrandom - Rust

If getrandom is not a direct dependency of your crate, you can still enable any of the above fallback behaviors by enabling the relevant feature in your root crate’s Cargo.toml:

[dependencies]
getrandom = { version = "0.2", features = ["js"] }

I guess that might be slightly unstable as an approach because getrandom is often a private dependency and if that got updated to a future getrandom 0.3, you'd need to manually update the version. On the other hand, as long as that dependency is only added in your application/binary crate, your lock file already prevents such breakage and keeping the thing up to date manually isn't too bad. Note that using * instead of 0.2 can lead to problems (targeting the wrong version) if a 0.3 version of getrandom is released but that version is not yet adopted in the intermediate crates you use. If in such a case at some time multiple of your dependencies use different versions of getrandom you might even need to add multiple versions of getrandom as a dependency and activate the feature on all of them.

1 Like

Thank you for your response. I think I misunderstood how the wild card version works.

AFAIK, that's already possible, though it would need all the crates in the hierarchy down to getrandom to support such flags.

I guess that might be slightly unstable as an approach because getrandom is often a private dependency and if that got updated to a future getrandom 0.3, you'd need to manually update the version.

I am more concerned about needing two different features set of the dependency but you can only target for one?

Sure, but that means libraries will have to add all of these dependencies features. Kind of puzzling.

On the other hand, as long as that dependency is only added in your application/binary crate, your lock file already prevents such breakage and keeping the thing up to date manually isn't too bad.

I think it's bad to edit the Cargo.lock file manually, it is thousands of file and does that mean I have to update it every time I change my dependencies?


I'm thinking a better design will be something like this

async-graphql {
 version = "2.10.0"
 subfeatures {
  getrandom = ["js"]
 }
}
1 Like

Ah, no, that's not what I meant. For applications you don't redo dependency resolution all the time, but instead you can usually use the lock file to decide manually when exactly you want to update dependencies, even within semver-compatible changes (by running cargo update). Then, if ever there is a getrandom 0.3 and one of your dependency updates to that, you're only getting that update by deliberately asking for it with the cargo update and if that happens to break your build, you can immediately follow up by adjusting the Cargo.toml to add a dependency on getrandom 0.3 with the js feature.

I'm not talking about manually editing lock file, but using it as it's (AFAIK) intended to be used just to control the point in time when dependency resolution is re-done and you're ready to handle any potential problems arising from that.

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.