Skip to content

Conversation

clarfonthey
Copy link

@clarfonthey clarfonthey commented Oct 8, 2025

This basically replaces the IsNormalized enum in the public API with two separate types:

  1. QuickCheck, which contains the Yes and Maybe cases
  2. NormalizationError, which contains the No case

(Note: IsNormalized::No could have been modified to accept an error argument instead, but since the Try trait is unstable, this would mean that ? would not be usable without a nightly-only flag for this crate.)

This allows a couple things:

  • If the lack of normalization is an error (e.g. a configuration format requires it), the position of the error can be pointed out to a user
  • Another error type can point to the normalization error as an underlying cause (if, for example, the error is "invalid string", this could explain why)
  • You can use the ? operator for normalization checks and encapsulate it in a Box<dyn Error> if desired

To accomodate this for the API, a few things were changed:

  • Quick-check functions don't take iterators, just strings. This way, the fact that they take either Chars or CharIndices can be left as an implementation detail. (The actual decomposition/recomposition APIs are still based upon iterators, but the check API is no longer so.)
  • The methods were renamed from is_* to check_*, and is_* aliases were added for the non-quick variants that still return bool

The naming normal_up_to for the error was chosen for parity with the libstd Utf8Error, which has valid_up_to as its index field.

I haven't actually benchmarked these changes, but I would be shocked if tracking the position substantially affected performance, especially if the result is ignored.

@clarfonthey clarfonthey force-pushed the errors branch 2 times, most recently from efa20e0 to c993dd1 Compare October 8, 2025 05:14
@clarfonthey
Copy link
Author

Right, core::error is a recent addition, so, it'll need a feature flag. I can add that in with some shenanigans so it always implements Error with std, but requires the extra feature if it's disabled.

@Manishearth
Copy link
Member

Seems like this would be a breaking change, yes? We haven't really been planning for a 0.2.0 of this crate but I guess it's possible. We can use the opportunity to increase MSRV, too, ideally to a version with the MSRV-aware resolver.

@clarfonthey
Copy link
Author

Seems like this would be a breaking change, yes? We haven't really been planning for a 0.2.0 of this crate but I guess it's possible. We can use the opportunity to increase MSRV, too, ideally to a version with the MSRV-aware resolver.

I was mostly being conservative on MSRV since I know people get super picky about it, but yes, this would be a breaking change. I tried to not make it too breaking, which is why there are still is_* aliases, but it is very strictly breaking by changing return types.

@Manishearth
Copy link
Member

If it's a breaking change anyway I think we can update MSRV to the Rust version that contains the MSRV-aware resolver.

I think what we should do then is make a last release in the 0.1.0 stream and have this be the main 0.2.0 change.

@clarfonthey clarfonthey force-pushed the errors branch 2 times, most recently from 5adde99 to 436836f Compare October 8, 2025 17:33
@clarfonthey
Copy link
Author

Sounds reasonable to me! I'm in no rush to get this merged, so, when you need me to make changes to accommodate whatever decisions get made, I'm down to do that.

For now, things seem to compile with the addition of an error feature that only implements the Error trait when that feature is enabled. With 1.36, that means it won't work if you have the specific combination of --no-default-features --features error, but every other combination works.

I believe that core::error was stabilised after the MSRV-dependent resolver, so, we might still want this, but if not, I can remove the feature and just make it always implement the trait.

@Manishearth
Copy link
Member

MSRV-aware resolver is 1.84, from January. The error trait is earlier. So yeah, remove that feature, and add the MSRV.

https://blog.rust-lang.org/2025/01/09/Rust-1.84.0/

I'd like to have this crate have an MSRV that's roughly a year old, and now that the MSRV-aware resolver is a thing bumping the MSRV isn't as annoying to users as it used to be.

@clarfonthey
Copy link
Author

clarfonthey commented Oct 9, 2025

Huh, for some reason I thought that feature was much older.

I decided to update rust-version and edition for this crate too to see what it would affect, and everything seems okay. Feel free to copy those changes to another PR if you'd prefer. I also set the crate version to 0.2.0 since you seemed keen on making this the main 0.2.0 change.

If you wanted to go with 1.85 instead, we could bump up to edition 2024, but since you mentioned only the MSRV-aware resolver, I only bumped to 2021 instead.

out.write("#[inline]\n")
out.write("pub fn is_public_assigned(c: char) -> bool {\n")
out.write(" match c {\n")
out.write(" matches!(\n")
Copy link
Author

Choose a reason for hiding this comment

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

This was a clippy suggestion, not required, but I figured I'd make it anyway. It's not stable in 1.36, so, that's why it wasn't being suggested before.

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.

2 participants