Add "div_rem" (divmod) function to std::num

I really want there to be a function to get both x / y and x % y at once in the standard library. Some reasons that I think this is justified as something to put in std:

  • This is a very commonly used function, for integers in particular. Think of indexing into multidimensional arrays (and similar regular data structures), splitting a work queue into fixed-size chunks (with some tasks left over), and tons of domain-specific tasks (budgeting, unit conversion, number theory, whatever).

  • It is trivial to implement for the primitive types, and cleaner for the user.

  • This is present in standard libraries for many different languages, such that many new users might expect it to be there.

  • It would be sad if several crates implemented slightly different versions of this. :cry:

So, here’s the trait that I was thinking of implementing (and a little test):

use std::ops::{Div,Rem};

trait DivRem<RHS = Self>: Div<RHS> + Rem<RHS> {
    fn div_rem(self, rhs: RHS)
               -> (<Self as Div<RHS>>::Output, <Self as Rem<RHS>>::Output);
}

impl DivRem for isize {
    fn div_rem(self, rhs: isize) -> (isize, isize) {
        let quotient = self / rhs;
        (quotient, self - (quotient*rhs))
    }
}

fn main() {
    println!("(+/+) {:?}", 9is.div_rem(8));
    println!("(+/-) {:?}", 9is.div_rem(-8));
    println!("(-/+) {:?}", (-9is).div_rem(8));
    println!("(-/-) {:?}", (-9is).div_rem(-8));
}

Output is of course:

(+/+) (1i, 1i)
(+/-) (-1i, 1i)
(-/+) (-1i, -1i)
(-/-) (1i, -1i)

Simple as this is, I know there’s always room for bikeshedding, (plus, discussion is educational for me) so here’s my justification for various decisions:

  1. I didn’t add this to existing traits like Int or Float, partly because those types are much more specific than I need, but mostly because I hoped that making a new trait would avoid backwards compatibility issues. If I can avoid trying to force an RFC through while 1.0 is still somewhat in flux, I would greatly prefer to leave the core team to more urgent matters like io/os stabilization. :wink:

  2. The method is div_rem instead of divmod because this is more consistent with Rust’s names and conventions, and I thought that that was the most important consideration. I do know that more people would recognize (and search for) divmod, however. Using the word divmod in the documentation might help with this.

  3. There’s no blanket implementation because we don’t have specialization, and an efficient impl is half the point. There’s no default implementation, because otherwise we would need Copy or Clone bounds, and I didn’t think it would be worth it.

  4. I didn’t use an associated type for the result because I felt like it was determined by the trait bounds and didn’t need to be specified by impls.

One sort-of unresolved question for me: it seems like this makes more sense in std::num than std::ops. But it’s only tied to the ops trait definitions. (Also, I’ve kind of wished that languages had a dedicated operator for this, but I don’t especially expect that to happen in Rust.)

3 Likes

This is already implemented as part of the Integer trait in num crate.

Hmm, I missed that. There are a couple of issues in my view though:

  1. This doesn’t belong on Integer. Unlike the factoring and floor operations, this applies to rationals, floats, complex numbers, and all kinds of fields.

  2. Div and Rem do not require the two operands to be the same type, but the num::integer implementation does.

I take it that the num crate is holding the old pieces that were split off from std::num? If it’s considered a nursery for ideas to be merged back into std::num, I can propose this change over there, but otherwise it’s a bit irrelevant, because I really think that this should be in (in fact should never have left) the standard library.

3 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.