rust-lang / rust-lang/libs-team
Add `rem_euclid_unsigned()` for signed integer types
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 178
- Forks
- 28
- Avg merge
- 15m
- Merged PRs (30d)
- 1
Description
Proposal
Problem statement
The current Euclidean remainder methods for iNN are defined as
fn rem_euclid(self, divisor: Self) -> Self
This is consistent with how most other arithmetic operations are typed, but it has a couple of consequences:
- The return type is signed but the result is always nonnegative by definition;
- Typical use cases (indexing an array) thus require an immediate cast to unsigned that can't fail or wrap but that can't be statically checked;
- There's an extra panic path for
self=Self::MINanddivisor= -1, eliminated by the proposed function; - For all other paths,
a.rem_euclid(b)is equivalent toa.rem_euclid(-b), making negative divisors redundant except for the niche case ofiNN::MIN; - But on the other hand, an unsigned divisor would have a lot of additional perfectly cromulent values even though the result may not be very interesting (
afor alla≥ 0,b- |a| for the rest.)
Motivating examples or use cases
A function that allows indexing slices with isize, giving modulo-len ringbuffer semantics which is often useful. In my case, it was about manipulating polygons where vertex (and edge) indices naturally wrap around and many algorithms are based on these semantics. Specifically, decrementing an usize index requires some ceremony because you can't just do i = (i-k).rem_euclid(len). (Note that the perhaps attractive-seeming option of using wrapping_sub() is wrong in general!)
fn index<T>(slice: &[T], i: isize) -> T {
slice[i.rem_euclid(self.len() as isize) as usize]
}
This implementation has two casts too many for my taste, neither of which can fail or wrap[^1] but the compiler doesn't know that. Using try_into() would just introduce noise. cast_signed/unsigned() may give a false impression that wraparound is possible.
[^1]: except if T is a ZST I guess.
Solution sketch
For all signed integer types, add
impl iNN {
pub fn rem_euclid_unsigned(self, divisor: uNN) -> uNN {
// notional, exposition only
if divisor <= iNN::MAX as uNN {
self.rem_euclid(divisor as iNN) as uNN;
} else if self >= 0 {
self as uNN
} else {
divisor - self.unsigned_abs()
}
}
}
Alternatives
- The usual: do nothing, let a possible third-party crate handle it.
- Add this, but with a NonNullUNN divisor which eliminates the remaining panicking path.
- Wait for pattern types and change (presumably backwards-compatibly) the return type of
rem_euclid()toiNN is 0... The divisor's type cannot be changed though. - Add a wrapper or method for slices that permits modulo-len indexing with
isize, handling the possibly most common use case.
Links and related work
i.r-l.o (four upvotes but no discussion as of this writing)
What happens now?
This issue contains an API change proposal (or ACP) and is part of the libs-api team feature lifecycle. Once this issue is filed, the libs-api team will review open proposals as capability becomes available. Current response times do not have a clear estimate, but may be up to several months.
Possible responses
The libs team may respond in various different ways. First, the team will consider the problem (this doesn't require any concrete solution or alternatives to have been proposed):
- We think this problem seems worth solving, and the standard library might be the right place to solve it.
- We think that this probably doesn't belong in the standard library.
Second, if there's a concrete solution:
- We think this specific solution looks roughly right, approved, you or someone else should implement this. (Further review will still happen on the subsequent implementation PR.)
- We're not sure this is the right solution, and the alternatives or other materials don't give us enough information to be sure about that. Here are some questions we have that aren't answered, or rough ideas about alternatives we'd want to see discussed.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the feature lifecycle linked in the issue and review the related internals discussion about rem_euclid_unsigned(). The issue names no files or tests; done is a libs-api decision on whether this API proposal and its alternatives are suitable for the standard library.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- api
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100