Open newpavlov opened 5 months ago
For the const case, as in your example, it's best to do that assertion at compile time:
const PAGE_SIZE: usize = 1 << 14;
const OS_PAGE_SIZE: usize = 1 << 12;
const _: () = {
assert!(PAGE_SIZE % OS_PAGE_SIZE == 0);
};
I recommend you provide a different example that shows how this method would be beneficial at runtime.
The point is that constant definitions with static assertions (we use them in our code, but they are not relevant here) can be quite far away from operations which depend on the assert. The motivation for the proposed methods is to clearly express that we expect that two numbers divide without remainder without any additional clutter associated with assert
s or branches.
Also, the second part of the example is for runtime values.
In that case, you should provide a const PAGES_PER_OS_PAGE
or something that does the division (and remainder check) ahead of time, and use that everywhere instead.
I missed the runtime example cfg.mapping_size
, but even in that case maybe you should do the division when you load the config and store the result in the cfg
struct rather than having divisions all throughout your codebase.
All that said, this seems pretty niche. Also for the name, I'm not a fan of norem
, maybe exact
or factor
instead?
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:
To simplify the code we have introduced the following helper trait and implemented it for necessary integer types:
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!
anduint_impl!
macros: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
https://github.com/rust-lang/rust/pull/116632