Skip to content

Pass Copy types by value#2404

Draft
tcharding wants to merge 13 commits intorust-bitcoin:masterfrom
tcharding:01-25-pass-by-value
Draft

Pass Copy types by value#2404
tcharding wants to merge 13 commits intorust-bitcoin:masterfrom
tcharding:01-25-pass-by-value

Conversation

@tcharding
Copy link
Copy Markdown
Member

@tcharding tcharding commented Jan 25, 2024

As discussed in #708 Copy types should be passed by value not by reference. Audit the whole codebase for reference arguments, check if they are Copy, and if so pass by value - phew.

Close #708
Done as part of #2153

@github-actions github-actions bot added C-bitcoin PRs modifying the bitcoin crate test labels Jan 25, 2024
@tcharding
Copy link
Copy Markdown
Member Author

tcharding commented Jan 25, 2024

If this is good, we should do the same in secp.

@tcharding tcharding force-pushed the 01-25-pass-by-value branch from 3d99298 to adbf102 Compare January 25, 2024 08:05
@tcharding tcharding added API break This PR requires a version bump for the next release release notes mention labels Jan 25, 2024
@coveralls
Copy link
Copy Markdown

coveralls commented Jan 25, 2024

Pull Request Test Coverage Report for Build 7749854763

  • -22 of 129 (82.95%) changed or added relevant lines in 22 files are covered.
  • 3 unchanged lines in 3 files lost coverage.
  • Overall coverage decreased (-0.003%) to 84.145%

Changes Missing Coverage Covered Lines Changed/Added Lines %
bitcoin/src/bip32.rs 6 7 85.71%
bitcoin/src/blockdata/witness.rs 2 3 66.67%
hashes/src/siphash24.rs 0 1 0.0%
bitcoin/src/blockdata/locktime/absolute.rs 7 9 77.78%
units/src/amount.rs 1 3 33.33%
bitcoin/src/blockdata/transaction.rs 6 9 66.67%
bitcoin/src/taproot/serialized_signature.rs 0 5 0.0%
bitcoin/src/crypto/key.rs 3 10 30.0%
Files with Coverage Reduction New Missed Lines %
bitcoin/src/blockdata/locktime/absolute.rs 1 64.27%
bitcoin/src/taproot/serialized_signature.rs 1 52.38%
hashes/src/siphash24.rs 1 97.45%
Totals Coverage Status
Change from base Build 7742657266: -0.003%
Covered Lines: 19328
Relevant Lines: 22970

💛 - Coveralls

@tcharding
Copy link
Copy Markdown
Member Author

Will fix CI tomorrow, I'm hosed right now.

@Kixunil
Copy link
Copy Markdown
Collaborator

Kixunil commented Jan 25, 2024

Concept ACK, will wait until CI fix.

@apoelstra
Copy link
Copy Markdown
Member

apoelstra commented Jan 25, 2024

Concept ACK

We should enable the trivially_copy_pass_by_ref clippy lint which enforces this. (Or maybe this PR already does, I haven't looked because you said you'd update it soon for CI :)).

@tcharding
Copy link
Copy Markdown
Member Author

I had a play with trivially_copy_pass_by_ref yesterday and all it did was tell me change millions of &self to self then after I did that clippy gave another million warnings about is_foo not being meant to take self. (I also had to configure breaking API lints to get the lint to work on pubic functions.) It also did not warn about any of the changes in this PR (IIRC).

@tcharding tcharding force-pushed the 01-25-pass-by-value branch from adbf102 to 60e2ad8 Compare January 26, 2024 00:14
@tcharding
Copy link
Copy Markdown
Member Author

tcharding commented Jan 26, 2024

This PR has shown that we (or at least I) don't have a single command that can be run to sanity check all the code in the the whole repo that doesn't take a whole age to run. Epic PITA.

@tcharding tcharding force-pushed the 01-25-pass-by-value branch from 60e2ad8 to 208c2ce Compare January 26, 2024 00:30
@junderw
Copy link
Copy Markdown
Contributor

junderw commented Jan 26, 2024

For types with a size less than 16 bytes or so I understand this, but for 32 byte arrays it might be a pretty big performance hit to continually copy the same 32 bytes over and over on the stack instead of an 8 byte pointer.

perhaps investigating perf hits caused by this might be warranted.

ie. Network enum is a strong ACK, but I’m still torn on all the private and public keys.

Thoughts?

@Kixunil
Copy link
Copy Markdown
Collaborator

Kixunil commented Jan 26, 2024

@junderw IIRC someone benchmarked it and the perf hit was very small. The copies are generally optimized away after inlining etc. I was thinking that it might be somewhat weird to do things like not accept references in methods called is_ and also even though perf hit is tiny, it's still more than zero. However there's I think strong argument for this: use of map becomes much more convenient since you can write .map(Foo::bar) instead of .map(|item| item.bar). This only applies to one-argument functions but having exception for multi-argument ones seems weird.

OTOH if you iterate over slices you need to use .copied or a closure. We could accept T: Borrow<Foo> but that seems too much and would probably cause troubles with type inference.

@tcharding
Copy link
Copy Markdown
Member Author

FTR This PR does not change an of instances of self/&self - that was on purpose because, as hinted at above with the lint stuff, I think the story around self is a bit more convoluted that other function parameters.

@tcharding
Copy link
Copy Markdown
Member Author

but for 32 byte arrays it might be a pretty big performance hit to continually copy the same 32 bytes over and over on the stack instead of an 8 byte pointer.

32 bytes is just 4 words (on my machine), who cares? If there are some benchmarks that prove this is slower then sure but this feels like something that the language and the compiler should be handling and the Rust convention seems to be if its Copy pass it by value. (I don't have citations off the top of my head though.)

@apoelstra
Copy link
Copy Markdown
Member

utACK 208c2ce8aae9c43ee1d685a87df9f2b8e23534e1 but I'm really struggling with whether we want to bring this in without machine tooling to keep it in sync. It'll be a mildly annoying breaking change for all our users and I'd rather only do it once.

@apoelstra
Copy link
Copy Markdown
Member

@tcharding I'm unsure if your diagnosis of Clippy is correct.

  • If I run cargo clippy -- -W clippy::trivially_copy_pass_by_ref (I also have to -A a couple other warnings but forget that for now..) then I only get a few things about private types.
  • If I then add avoid-breaking-exported-api = false to my clippy.toml, then the interesting stuff happens -- I get a lot of warnings. Some of which are suggesting that I change to_ methods from &self to self.
  • I also get some warnings about to_ methods taking self, but it appears these are not the methods I just changed. They're separate, valid warnings, that clippy had been ignoring because they'd break our public API, but which appear to be legit.

Let me know if I'm wrong; there is a long list of warnings and I didn't fix them all so it's possible I'm getting mixed up reading the churn.

@tcharding
Copy link
Copy Markdown
Member Author

Thanks for looking, I'll have another play with it.

@github-actions github-actions bot added C-hashes PRs modifying the hashes crate C-units PRs modifying the units crate C-internals PRs modifying the internals crate C-io PRs modifying the io crate labels Jan 30, 2024
@tcharding
Copy link
Copy Markdown
Member Author

Props to you @apoelstra, you were correct. I've added a bunch more patches to this PR.

@tcharding
Copy link
Copy Markdown
Member Author

I've had enough of this for one day, will fix CI tomorrow.

@tcharding tcharding force-pushed the 01-25-pass-by-value branch from 2d5602f to 84d5877 Compare January 31, 2024 03:35
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

In b810f1b92accb456699167502ba923583947822e:

Can just drop the ref here rather than adding a *.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Done as suggested.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

In 186e94afb452a65939b07ec0f8273338a3ec7055:

We might as well deprecate the old one. We broke the shit out of everybody by changing the signatures of all those other functions, but here we can give them a little relief since we're renaming stuff.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Done, now.

The siphash hash is 64 bits and is `Copy`, its conversion function
should therefore be named `to_u64`.
`OutPutType` is `Copy`, pass it by value.

Found with:

 cargo +nightly clippy -- -W clippy::trivially_copy_pass_by_ref
`OutOfRangeError` is `Copy`, pass it by value.

Found with:

 cargo +nightly clippy -- -W clippy::trivially_copy_pass_by_ref
Configure `clippy` with `avoid-breaking-exported-api = false` and then
configure each crate to warn for trvial pass by reference.

Fix all the predicate warnings by passing `self` by value for types that
implement `Copy`.

Add a single `allow` for our error type which needs to consume the inner
error.
@tcharding tcharding dismissed stale reviews from clarkmoody and apoelstra via 8184ddf February 2, 2024 01:02
@tcharding tcharding force-pushed the 01-25-pass-by-value branch from facb03a to 8184ddf Compare February 2, 2024 01:02
@tcharding
Copy link
Copy Markdown
Member Author

tcharding commented Feb 2, 2024

Rebase only, no other changes.

Copy link
Copy Markdown
Member

@apoelstra apoelstra left a comment

Choose a reason for hiding this comment

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

ACK 8184ddf

@tcharding tcharding added this to the 0.32.0 milestone Feb 5, 2024
@tcharding
Copy link
Copy Markdown
Member Author

This PR is totally non-urgent, can be re-done at any time.

@tcharding tcharding marked this pull request as draft February 12, 2024 22:44
@tcharding
Copy link
Copy Markdown
Member Author

On ice till next release.

@tcharding tcharding removed this from the 0.32.0 milestone Mar 14, 2024
@tcharding tcharding mentioned this pull request Jun 14, 2024
apoelstra added a commit that referenced this pull request Jun 14, 2024
9f01871 api: Run just check-api (Tobin C. Harding)
7929b51 Pass keys by value (Tobin C. Harding)

Pull request description:

  We should pass `Copy` types by value not by reference. Pass the key types by value.

  This is patch 1 from #2404

ACKs for top commit:
  apoelstra:
    ACK 9f01871 this will annoy some people but I think we should do it

Tree-SHA512: 18afab537edf4ade4dc1c1e5992e50060b8935531f1e3cbe1d3b94b2fcb87aafa39947f342e0e762835bda3b4091dd35b3b74ea79f4dbb3b21660ffd21d1f82e
apoelstra added a commit that referenced this pull request Jun 22, 2024
433fd6b api: Run just check-api (Tobin C. Harding)
8fd583b Pass hash types by value (Tobin C. Harding)

Pull request description:

  We should pass `Copy` types by value not by reference. Pass the hash types by value.

  Second step in the pass-copy-types-by-value work, pulled out of #2404.

ACKs for top commit:
  apoelstra:
    ACK 433fd6b
  Kixunil:
    ACK 433fd6b

Tree-SHA512: 999d12f60550cacc4ae19b4cbf505b25c1eed803820f22b1a706e9f95da1b7e7b422f393f4115d579927c0c476cd504036a39b3cdc06a1d6befbcff5513f7433
apoelstra added a commit that referenced this pull request Jun 28, 2024
dc10a49 api: Run just check-api (Tobin C. Harding)
5e8f204 Pass sigs and associated types by value (Tobin C. Harding)

Pull request description:

  We should pass `Copy` types by value not by reference.

  Currently this is not done in secp, but lets do it here in bitcoin.

  Pass by value:

  - `SerializedSignature`
  - bitcoin sigs
  - secp sigs
  - secp `Message`

  This is a continuation of the work to split up #2404 into manageable PRs.

ACKs for top commit:
  apoelstra:
    ACK dc10a49
  Kixunil:
    ACK dc10a49

Tree-SHA512: 8736eba067c74edb951c92357f5b3d0fc99c4fa6dc3beea579c10b3150873b74e8ec46c2c01f18818b37fca6e77c6b6edddeb6340edde6a9d8c28a4e69164c8c
@tcharding
Copy link
Copy Markdown
Member Author

Hey @mpbagot do you feel like checking if we need to bring any of this back to life? By that I mean do we need to do any API changes, specifically in the stabalizing crates, to pass by value. Doing so requires digging into clippy usage and is probably non-trivial to do. Only pick this up if you want to and you have capacity. Might be worth thinking about in relation to the key changes you've been doing also.

@apoelstra
Copy link
Copy Markdown
Member

8184ddf needs rebase

@tcharding
Copy link
Copy Markdown
Member Author

8184ddf needs rebase

lol, I'd smack that bot if my arm was long enough.

@mpbagot
Copy link
Copy Markdown
Contributor

mpbagot commented Mar 27, 2026

Hey mpbagot do you feel like checking if we need to bring any of this back to life? By that I mean do we need to do any API changes, specifically in the stabalizing crates, to pass by value.

The only changes that I could find were block_hash() on Header, combine() on the MerkleNode types and is_valid() + is_error() on NumOpResult. If hashes is included, then hash_again() on the sha256 hash too.
That said, should Header be Copy? It's convenient to be so, but it's an 80 byte type, which makes it very expensive if the compiler ends up copying it in memory.

Doing so requires digging into clippy usage and is probably non-trivial to do.

The Clippy lint you used here (trivially_copy_pass_by_ref) doesn't seem to trigger anymore. I tried changing various functions on Copy types and enabling the lint and Clippy refused to complain.

Might be worth thinking about in relation to the key changes you've been doing also.

I'll take a look at this too.

@tcharding
Copy link
Copy Markdown
Member Author

Hey mpbagot do you feel like checking if we need to bring any of this back to life? By that I mean do we need to do any API changes, specifically in the stabalizing crates, to pass by value.

The only changes that I could find were block_hash() on Header, combine() on the MerkleNode types and is_valid() + is_error() on NumOpResult. If hashes is included, then hash_again() on the sha256 hash too. That said, should Header be Copy? It's convenient to be so, but it's an 80 byte type, which makes it very expensive if the compiler ends up copying it in memory.

I"m a bit lost, I looked a bunch of these up on master and they don't take non-Self args?

I think Header should be Copy (as it is). 80 bytes isn't thaaat much and its never getting any bigger. But I'm not an optimisation expert by any means.

Doing so requires digging into clippy usage and is probably non-trivial to do.

The Clippy lint you used here (trivially_copy_pass_by_ref) doesn't seem to trigger anymore. I tried changing various functions on Copy types and enabling the lint and Clippy refused to complain.

WIN. Thanks for investigating man.

Might be worth thinking about in relation to the key changes you've been doing also.

I'll take a look at this too.

@mpbagot
Copy link
Copy Markdown
Contributor

mpbagot commented Mar 27, 2026

I"m a bit lost, I looked a bunch of these up on master and they don't take non-Self args?

Is that not part of the problem here? In this PR (93194aa), you made changes to functions that took only self by ref on a Copy type. I thus assumed this was part of the issue you were wanting to solve.

@apoelstra
Copy link
Copy Markdown
Member

The Clippy lint you used here (trivially_copy_pass_by_ref) doesn't seem to trigger anymore.

Did you enable the "api break" config parameter for Clippy? Without it it won't lint on anything in the public API.

@mpbagot
Copy link
Copy Markdown
Contributor

mpbagot commented Mar 30, 2026

I did not, which was an oversight. I have tested everything again now with that config parameter, and I still can't trigger the trivially_copy_pass_by_ref lint. I can trigger wrong_self_convention for is_valid + is_error, which I could not before, so the config appears to be working.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

API break This PR requires a version bump for the next release C-bitcoin PRs modifying the bitcoin crate C-hashes PRs modifying the hashes crate C-internals PRs modifying the internals crate C-io PRs modifying the io crate C-units PRs modifying the units crate release notes mention test

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Should 32 byte Copy types be pass by reference or value?

7 participants