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

Implement PartialEq for PyBytes and [u8] #4259

Merged
merged 10 commits into from
Jun 22, 2024

Conversation

codeguru42
Copy link
Contributor

This is my attempt to implement PartialEq for PyBytes and [u8]. See #4250.

Copy link
Contributor Author

@codeguru42 codeguru42 left a comment

Choose a reason for hiding this comment

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

This is my first draft for this PR. At this point, I just want to be sure I'm heading in the right general direction. I realize the meat of the implementation is incomplete. I have a couple comments/questions to fill in some gaps for me.

src/types/bytes.rs Outdated Show resolved Hide resolved
impl PartialEq<[u8]> for Borrowed<'_, '_, PyBytes> {
#[inline]
fn eq(&self, other: &[u8]) -> bool {
self.to_cow().map_or(false, |s| s == other)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

From what I can tell, all the other impls are just permutations of different types that ultimate call this impl. I have two questions here:

  1. I copied this from types/string.py. But I removed the special implementation for 3.10 or ABI. I assume that is specific to the string implementation. Is that correct? Or is there an analogous implementation for bytes that is special for 3.10 or ABI?
  2. I'm pretty sure to_cow() isn't a thing for PyBytes. I will work on an implementation here. Do I manually map (or loop) over the two parameters and compare each element? Or is there something more idiomatic in Rust?

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 you can use self.as_bytes() == other here (which should then use the PartialEq impl on slices on std)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@Icxolu Thanks for the feedback. I tried your suggestion and tests fail with

no implementation for `instance::Bound<'_, bytes::PyBytes> == &[u8; 12]

I guess we need the length of the array as part of the type? Or should I use Vec<u8> instead?

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, this is because let b = b"hello, world"; is creating an array (&[u8, 12]), not a slice (&[u8]). To test these impl you can turn that array into a slice like this: let b = b"hello, world".as_slice();

If we want we can also add implementations for arrays directly as well, using const generics, something like the following:

impl <const N: usize> PartialEq<[u8; N]> for Borrowed<'_, '_, PyString> { ... }

Copy link
Contributor Author

Choose a reason for hiding this comment

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

...or are my tests wrong and need changed?

Copy link
Contributor

Choose a reason for hiding this comment

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

They currently test comparison with arrays (which are nor implemented) instead of the comparison with slices. This can be a bit surprising, since often arrays can be automatically coerced to slices by the compiler. In this case the compiler is not smart enough to see that this is what we want.

If you change your test to use let b = b"hello, world".as_slice(); or let b: &[_] = b"hello, world";, we explicitly specify that we want b to be a slice as opposed to an array. This way it should then also use the PartialEq impl that you have implemented here.

Copy link
Contributor Author

@codeguru42 codeguru42 Jun 17, 2024

Choose a reason for hiding this comment

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

For context, your first response wasn't showing when I typed my previous comment.

@codeguru42
Copy link
Contributor Author

From what I can tell you have to specify the length of the array like [u8; 12]. Is there a way to make the length generic or should I use Vec<u8> instead?

impl PartialEq<[u8]> for Borrowed<'_, '_, PyBytes> {
#[inline]
fn eq(&self, other: &[u8]) -> bool {
self.to_cow().map_or(false, |s| s == other)
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 you can use self.as_bytes() == other here (which should then use the PartialEq impl on slices on std)

src/types/bytes.rs Outdated Show resolved Hide resolved
src/types/bytes.rs Outdated Show resolved Hide resolved
@codeguru42
Copy link
Contributor Author

This is working for PartialEq for PyBytes and [u8]. Do we want to merge this as-is or should I also add impls for [u8; N] as suggested above by icxolu?

@codeguru42
Copy link
Contributor Author

I think I fixed one of the examples in the doc comment. I'm not sure how to fix the second one, though.

Copy link
Contributor

@Icxolu Icxolu left a comment

Choose a reason for hiding this comment

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

This is working for PartialEq for PyBytes and [u8]. Do we want to merge this as-is or should I also add impls for [u8; N] as suggested above by icxolu?

I would suggest we start with what we have here and if there is demand for the array comparisons, we can add them later as a followup.

src/types/bytes.rs Outdated Show resolved Hide resolved
Co-authored-by: Icxolu <[email protected]>
Copy link
Member

@davidhewitt davidhewitt left a comment

Choose a reason for hiding this comment

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

Looks great, thanks! Will merge now and rebase the 0.22 release on it tomorrow 👍

@davidhewitt davidhewitt added this pull request to the merge queue Jun 22, 2024
@@ -0,0 +1 @@
Implement `PartialEq<str>` for `Bound<'py, PyBytes>`.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
Implement `PartialEq<str>` for `Bound<'py, PyBytes>`.
Implement `PartialEq<[u8]>` for `Bound<'py, PyBytes>`.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I saw this but wasn't able to accept it in time before the merge.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll fix it on my next PR for #4245

Copy link
Member

@davidhewitt davidhewitt Jun 24, 2024

Choose a reason for hiding this comment

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

No prob; I got it in #4266 thanks to @alex

@alex
Copy link
Contributor

alex commented Jun 22, 2024

Changelog is wrong, so let's remember to fix when we do the release!

Merged via the queue into PyO3:main with commit 908ef6a Jun 22, 2024
41 checks passed
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.

4 participants