Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add bitcoin_hashes features #408

Closed

Conversation

tcharding
Copy link
Member

@tcharding tcharding commented Feb 21, 2022

We re-export bitcoin_hashes so users do not have to depend directly on
it. This is Rust best practice as makes keeping versions in sync easier.

Currently, however, it is not possible to enable different features in
bitcoin_hashes and our feature enables it with no default features -
this makes the re-export basically useless.

Add a bunch of features that can be used to turn on the various
bitcoin_hashes features.

Note

The re-export/feature problem is not specific to this repo, I'd think there was a standard way to solve this. The solution presented here is made up by me so I'm inclined to think there is probably a better, more standard, solution. Hence this PR is draft for feedback please. We suffer the same problem all the way up our stack and its going to get worse when we start crate smashing.

Done while investigating: rust-bitcoin/rust-bitcoin#819 (comment)

We re-export `bitcoin_hashes` so users do not have to depend directly on
it. This is Rust best practice as makes keeping versions in sync easier.

Currently, however, it is not possible to enable different features in
`bitcoin_hashes` and our feature enables it with no default features -
this makes the re-export basically useless.

Add a bunch of features that can be used to turn on the various
`bitcoin_hashes` features.
bitcoin-hashes-alloc = ["bitcoin_hashes/alloc"]
bitcoin-hashes-std-serde = ["bitcoin_hashes/std", "bitcoin_hashes/serde-std"]
bitcoin-hashes-alloc-serde = ["bitcoin_hashes/alloc", "bitcoin_hashes/serde"]
bitcoin-hashes-std-schemars = ["bitcoin_hashes/std", "bitcoin_hashes/schemars"]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're going to reexport things I think it only makes sense to reexport features that are actually used in conditions here. In this case bitcoin-hashes-std. However I suspect your approach may help with forward compatibility. Is it worth 2^n complexity, though?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also am very uncomfortable with the 2^n complexity here. I've run into this problem many times when using re-exports from the rust-bitcoin dependency tree but I don't think it's reasonable to try and solve it this way. (This is only one dependency from one crate, and it brings in 5 new feature gates!)

@@ -28,9 +28,18 @@ recovery = ["secp256k1-sys/recovery"]
lowmemory = ["secp256k1-sys/lowmemory"]
global-context = ["std"]

# Give users full control of bitcoin_hashes features while stil using the secp256k1 re-exported
# bitcoin_hashes dependency.
bitcoin-hashes-std = ["bitcoin_hashes/std"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should activate this with our std feature.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additionally, once we are in a higher MSRV, we can use "weak-dependency" features here then!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we batch these together with our features, the list shouldn't explode as much.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We would have to mandate bitcoin_hashes in combination with std which doesn't seem right. Weak dependencies will take optimistically about a year and a half to be allowed here. (But once they are, hell yeah, let's use them.)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We would have to mandate bitcoin_hashes in combination with std which doesn't seem right.

Yes but also, the cost is negligible thought, isn't it? Esp. when weighted against the API complexity of all these features.

My vote would go down for either:

  • Tie all bitcoin_hashes features to the respective bitcoin features
  • Force users to depend on bitcoin_hashes separately if they want to activate features

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what do you mean by "Tie all bitcoin_hashes features to the respective bitcoin features". Note that we're in secp256k1 repo.

Force users to depend on bitcoin_hashes separately if they want to activate features

We can't do this because we don't get to learn which features were activated in bitcoin_hashes. I already tried this in my own project and failed.

But now I also remembered one super-annoying issue:

  • Crate A has std feature to impl std::error::Error
  • Crate B has same std feature as A, optionally depends on A, and has an error type that has an error type from A as a source
  • Crate C depends on B with std, without A
  • Crate D depends on B without std with A

The compilation will fail because A/std is not enabled so the impl is missing but B tries to use it because std is enabled.
The only solution to this (other than future Rust version) I can think of is don't return Some from source when A is activated without a special feature - one exactly like in this PR.

Actually we should check for these cases in all the crates...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that we're in secp256k1 repo.

Whoops my bad, I meant secp256k1 obviously. With tie all features, I meant:

[features]
std = ["bitcoin_hashes/std"]
  • Crate C depends on B with std, without A

  • Crate D depends on B without std with A

Features get unified across a dependency-graph so we should end up with B/std and A, no?

  • Crate B has same std feature as A, optionally depends on A, and has an error type that has an error type from A as a source

We shouldn't have cfg'd enum variants if that is what you are describing here. Those will cause headache across dependency graphs!

I think the Rust idiomatic way is to forward all features to your dependencies, i.e.

std feature in crate A enables an std feature (if present) in all its dependencies. Up until "weak-dependency features" land and/or can be used, this will automatically enable optional dependencies if you enable such a feature. That is a price we need to evaluate whether it is worth paying. Our dependency graph is pretty small anyway so this might not be a big deal on the whole. YMMV.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Features get unified across a dependency-graph so we should end up with B/std and A, no?

Only if we do it like you suggest which has the downside of potentially pulling in unneeded dependency.

We shouldn't have cfg'd enum variants if that is what you are describing here.

That's not the issue and there are several ways to deal with it even without #[non_exhaustive] (but we will bump MSRV so #[non_exhaustive] is the way to go. So no, that's not the issue. The issue is impl std::error::Error for MyError conditional on std feature.

forward all features

IMO there's no great solution today. There are multiple bad solutions, forwarding only required features seems the least bad.

@Kixunil
Copy link
Collaborator

Kixunil commented Feb 22, 2022

Why was this closed?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants