rust-lang / rust-lang/libs-team

ACP: convenient numeric conversions dependent on overflow-checks

Open
#811 9 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

ACP-accepted api-change-proposal
Dominant language
Rust
Stars
178
Forks
28
Avg merge
15m
Merged PRs (30d)
1

Description

Proposal

Problem statement

Currently the default way when converting between numeric types is using as. However so many behaviors are encoded in this one little word. There long have been calls for dedicated functions which encode intent better.

In recent work (part accepted, part proposed) I've tried to make this situation better through _cast methods for integers, and to_{float|int} methods:

However, even once these become commonplace there is still a common need unfilled. Namely, when one has a fallible conversion (e.g. narrowing integer, or float -> int) when you are writing performance sensitive code.

Today, you would still use as for this. However this means that even in debug mode, if something went wrong and your fallible conversion failed, you have no way of knowing.

Motivating examples or use cases

As an example, say I am writing some sort of graphics processing code, where I have colors in f32s, and am converting to u8:

for (out, color) in img.iter_mut().zip(colors) {
    out.r = (color.r * 255.0 + 0.5) as u8;
    out.g = (color.g * 255.0 + 0.5) as u8;
    out.b = (color.b * 255.0 + 0.5) as u8;
}

I "know" that my colors are well-behaved and in 0..1, so I want this to run at maximum speed, so I use as as to not block autovectorization or add extra checks. But there's a bug in my program, and silently some color component is actually 1.5, or NaN. With as I would have no way of knowing. With the proposed API below I would write:

for (out, color) in img.iter_mut().zip(colors) {
    out.r = (color.r * 255.0 + 0.5).to_int();
    out.g = (color.g * 255.0 + 0.5).to_int();
    out.b = (color.b * 255.0 + 0.5).to_int();
}

and get maximum speed in release mode, while still getting a panic when running in debug mode for my broken assumptions.

Solution sketch

I propose the following set of functions to be added to all primitive integer and floating point types respectively:

impl IntT {
    /// When overflow-checks is enabled this is equivalent to `strict_cast`,
    /// without this is equivalent to `as`.
    fn cast<Int: CheckedCastFromInt<Self>>(self) -> Int;
}

impl FloatT {
    /// When overflow-checks is enabled this is equivalent to `to_int_strict`,
    /// without this is equivalent to `as`.
    fn to_int<Flt>(self) -> Flt
    where Self: FloatToInt<Flt>;
}

Note that to_int_strict is from my other proposal: https://github.com/rust-lang/libs-team/issues/810.

Open questions

  • Will these methods being the shortest and most convenient wording lead to people reaching for these even if they ought to be using the strict_ versions for fallible conversions, even in release mode?

Alternatives

One can simply write as, but this gives no errors in debug mode. The 'proper' fallible methods are no real alternative at all as their checks can block autovectorization, or otherwise be needlessly expensive as we 'know' our conversion ought to be infallible based on preconditions.

Links and related work

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading the linked libs-team issues 788 and 810, then compare their proposed conversion APIs with the as examples and the overflow-checks behavior described here. The work is done when the API shape and its interaction with strict conversions and release-mode performance have been resolved.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
developer-experience
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.