Skip to content

Add (checked_)norem_div methods for integer types #337

Open
@newpavlov

Description

Proposal

Problem statement

In some cases it desirable to get division result and ensure that the division has no remainder. For example, a function which works with memory mappings may accept size in bytes, but it requires that the size must be multiple of OS page size. Today we have to insert separate checks (e.g. assert_eq!(size / OS_PAGE_SIZE, 0)) somewhere near the division. It makes code less concise and not great at expressing intent.

Motivating examples or use cases

In our code we have code which works with pages and accepts size in bytes. It requires that the size should be multiple of the page size. The size is provided in bytes for user convenience and because page size can depend on OS and constants.

So we have a bunch of code like this:

const PAGE_SIZE: usize = 1 << 14;
const OS_PAGE_SIZE: usize = 1 << 12;

// somewhere else in the project
let os_pages: usize = pages_len * (PAGE_SIZE / OS_PAGE_SIZE);
// the constants defined in a separate faraway module,
// so to make correctness reasoning local, we assert it near the division
assert_eq(PAGE_SIZE % OS_PAGE_SIZE, 0);

// another place
let pages_to_map: usize = cfg.mapping_size / PAGE_SIZE;
if cfg.mapping_size / PAGE_SIZE {
    return Err(Error::NotPageMultiple);
}
let pages_to_map: u32 = pages_to_map.try_into().map_err(|_| Error::TooBig)?;

To simplify the code we have introduced the following helper trait and implemented it for necessary integer types:

trait NoRemDiv {
    fn checked_noremdiv(self, other: Self) -> Option<Self>;
    fn noremdiv(self, other: Self) -> Self { self.checked_noremdiv(other).unwrap() }
}

let os_pages = pages_len * PAGE_SIZE.norem_div(OS_PAGE_SIZE);

let pages_to_map: u32 = cfg
    .mapping_size
    .checked_noremdiv(PAGE_SIZE)
    .ok_or(Error::NotPageMultiple)?
    .try_into()
    .map_err(|_| Error::TooBig)?;

It's better than the explicit checks, but we need to introduce the helper trait and import it every time the methods are used. Also in a multi-crate projects it becomes even more annoying. We either have to define the trait in each crate where it needed or introduce a whole separate crate for this small functionality.

Solution sketch

Introduce the following methods to all integers through int_impl! and uint_impl! macros:

/// Checked integer division without remainder. Computes `self / rhs`,
/// returning `None` if `rhs == 0` or if division remainder is not zero.
pub const fn checked_norem_div(self, rhs: Self) -> Option<Self> { ... }

/// Integer division without remainder. Computes `self / rhs`,
/// panics if `rhs == 0` or if division remainder is not zero.
pub const fn norem_div(self, rhs: Self) -> Self { ... }

For signed integer the conditions also include potential overflow when MIN is divided by -1.

Alternatives

Users may use explicit checks based on the remainder operator or custom traits as shown in the examples section. Arguably, the functionality is too small for a separate crate.

Links and related work

rust-lang/rust#116632

Metadata

Assignees

No one assigned

    Labels

    T-libs-apiapi-change-proposalA proposal to add or alter unstable APIs in the standard libraries

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions