# Pre-RFC: Change abs() to return unsigned integers

**URL:** <https://internals.rust-lang.org/t/pre-rfc-change-abs-to-return-unsigned-integers/1747>\
**Category:** libs\
**Created:** [March 23, 2015, 6:02pm UTC](https://internals.rust-lang.org/t/pre-rfc-change-abs-to-return-unsigned-integers/1747 "2015-03-23T18:02:30Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![pitdicker](https://avatars.discourse-cdn.com/v4/letter/p/bbe5ce/32.png) [@pitdicker](https://internals.rust-lang.org/u/pitdicker)\
**Post date:** [March 23, 2015, 6:02pm UTC](https://internals.rust-lang.org/t/pre-rfc-change-abs-to-return-unsigned-integers/1747/1 "2015-03-23T18:02:30Z")

</div>

# Summary

Change the function `abs()` to return unsigned integers, instead of signed integers as it does today.

# Motivation

The documentation for `std::num::SignedIntStable::abs()` states, `Int::min_value()` will be returned if the number is `Int::min_value()`. This subtlety can lead to bugs, because a developer may count on `abs()` to return a positive value. And forcing every caller of `abs()` to know about this corner case and handle it, is not a very nice design.

If `abs()` is changed to return an unsigned integer of the same width as its input, there is no special case to handle. Code calling `abs()` is cleaner because it does not have to handle `Int::min_value()`, and will lead to fewer bugs.

Because (for example) `-127i8` happens to have the same binary representation as `128u8`, this is basically a free operation.

Consider how code that calls the current version of `abs()` can ensure its result is positive:

1. Proof by reasoning about your code that the input of `abs()` will never be `Int::min_value()`.
2. Do a runtime check before calling `abs()`, and somehow handle it or return an error.
3. Cast the input to a wider signed integer (if available) before calling `abs()`.
4. Cast the result of `abs()` to an unsigned integer of the same size.

In all cases this requires extra work for the code calling abs. With this proposal option 4 will be the default, while option 1 to 3 remain possible. The default behaviour will lead to correct (positive) results for all inputs.

# Drawbacks

It is not possible anymore to do:

```rust
let mut a: i8 = -15;
a = abs(a);

```

This becomes:

```rust
let mut a: i8 = -15;
a = abs(a) as i8; // may overflow

```

This may actually be an advantage, as now the user code is the cause of the overflow, and not `abs()`.

# Alternatives

Keep the status quo, which can lead to subtle bugs or code that is less clean.

* * *

Basically this is not a new idea, see [https://github.com/rust-lang/rust/issues/2353](https://github.com/rust-lang/rust/issues/2353) and [http://www.reddit.com/r/rust/comments/2cykyz/abs\_for\_minimum\_int\_values/](http://www.reddit.com/r/rust/comments/2cykyz/abs_for_minimum_int_values/). Is this the proper way to suggest such a change? Would it still be possible before 1.0 (abs is not yet stable)? This is a trivial change, and I have a working sample implementation.

---

<div class="post-metadata">

**Author:** ![bwo](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/bwo/32/5414_2.png) [@bwo](https://internals.rust-lang.org/u/bwo)\
**Post date:** [March 23, 2015, 6:28pm UTC](https://internals.rust-lang.org/t/pre-rfc-change-abs-to-return-unsigned-integers/1747/2 "2015-03-23T18:28:22Z")

</div>

> [@pitdicker](#):
>
> Because (for example) -127i8 happens to have the same binary representation as 128u8

Why would `abs(-127i8)` return `128u8` instead of `127u8`?

---

<div class="post-metadata">

**Author:** ![pitdicker](https://avatars.discourse-cdn.com/v4/letter/p/bbe5ce/32.png) [@pitdicker](https://internals.rust-lang.org/u/pitdicker)\
**Post date:** [March 23, 2015, 6:33pm UTC](https://internals.rust-lang.org/t/pre-rfc-change-abs-to-return-unsigned-integers/1747/3 "2015-03-23T18:33:29Z")

</div>

You are completely right, I meant abs(-128i8) has the same binary representation as 128u8.

---

<div class="post-metadata">

**Author:** ![comex](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/comex/32/2587_2.png) [@comex](https://internals.rust-lang.org/u/comex)\
**Post date:** [March 23, 2015, 7:57pm UTC](https://internals.rust-lang.org/t/pre-rfc-change-abs-to-return-unsigned-integers/1747/4 "2015-03-23T19:57:41Z")

</div>

┈┈┈ 👍┈┈╌ 👍👍┈┈┈:+1:👍┈┈╌ 👍👍┈┈╌:+1:👍👍👍👍👍✊✊👍👍👍👍✊✊✊✊👍👍👍✊✊✊✊👍👍👍✊✊✊✊✊👍👍👍✊✊✊✊👍👍👍✊✊✊✊✊👍👍👍👍✊✊✊✊

---

<div class="post-metadata">

**Author:** ![oli-obk](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/oli-obk/32/3168_2.png) [@oli-obk](https://internals.rust-lang.org/u/oli-obk)\
**Post date:** [March 24, 2015, 12:29pm UTC](https://internals.rust-lang.org/t/pre-rfc-change-abs-to-return-unsigned-integers/1747/5 "2015-03-24T12:29:06Z")

</div>

A small analysis of the usage of `abs` in the Rust source code:

There are a total of 196 uses of `abs`. Only 11 are not called on float types. These 11 usages are displayed below.

Doc examples:

```rust
//! impl PartialEq for FuzzyNum {
//! // Our custom eq allows numbers which are near each other to be equal! :D
//! fn eq(&self, other: &FuzzyNum) -> bool {
//! (self.num - other.num).abs() < 5
//! }
//! }

```

```rust
/// let a = [-3, 0, 1, 5, -10];
/// assert_eq!(*a.iter().max_by(|x| x.abs()).unwrap(), -10);

```

```rust
/// let a = [-3, 0, 1, 5, -10];
/// assert_eq!(*a.iter().min_by(|x| x.abs()).unwrap(), 0);

```

In unit tests:

```rust
struct SketchyNum {
    num : int
}
impl PartialEq for SketchyNum {
    // Our custom eq allows numbers which are near each other to be equal! :D
    fn eq(&self, other: &SketchyNum) -> bool {
        (self.num - other.num).abs() < 5
    }
}

```

```rust
let xs: &[int] = &[-3, 0, 1, 5, -10];
assert_eq!(*xs.iter().max_by(|x| x.abs()).unwrap(), -10);

```

```rust
let xs: &[int] = &[-3, 0, 1, 5, -10];
assert_eq!(*xs.iter().min_by(|x| x.abs()).unwrap(), 0);

```

In the “literal out of range” lint it is already expected to end up as an `u64`, which means this `as u64` would simply be a widening cast

```rust
if (negative && v > (min.abs() as u64)) ||
   (!negative && v > (max.abs() as u64)) {
    cx.span_lint(OVERFLOWING_LITERALS,
                 e.span,
                 &*format!("literal out of range for {:?}", t));
    return;
}

```

Unit tests for `abs` would be somewhat more complicated as they currently expect the result to be of the same type.

```rust
assert!((1 as $T).abs() == 1 as $T);
assert!((0 as $T).abs() == 0 as $T);
assert!((-1 as $T).abs() == 1 as $T);

```

---

<div class="post-metadata">

**Author:** ![pitdicker](https://avatars.discourse-cdn.com/v4/letter/p/bbe5ce/32.png) [@pitdicker](https://internals.rust-lang.org/u/pitdicker)\
**Post date:** [March 24, 2015, 5:52pm UTC](https://internals.rust-lang.org/t/pre-rfc-change-abs-to-return-unsigned-integers/1747/6 "2015-03-24T17:52:07Z")

</div>

@ker: thanks for the analysis!

On my first try at analysing the rust source, I did not find any useful calls to abs. If I ignore the docs and unit tests (which have controlled input and are easely fixable if nessesary) 3 are left.

FuzzyNum and SketchyNum use abs() to calculate the difference between 2 integers. The way they do it can easely lead to overflows, and improving abs does not do much to help. But I guess that is ok with fuzzy and sketchy numbers :).

The “literal out of range” lint is more interesting, as it uses option number 4, and would be helped by this proposal.

---

<div class="post-metadata">

**Author:** ![milesrout](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/milesrout/32/383_2.png) [@milesrout](https://internals.rust-lang.org/u/milesrout)\
**Post date:** [April 1, 2015, 12:29am UTC](https://internals.rust-lang.org/t/pre-rfc-change-abs-to-return-unsigned-integers/1747/7 "2015-04-01T00:29:05Z")

</div>

I think it’s pretty logically clear that `abs` should return an unsigned integer if given an integer. It is by definition an unsigned value.

---

<div class="post-metadata">

**Author:** ![rkjnsn](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/rkjnsn/32/5368_2.png) [@rkjnsn](https://internals.rust-lang.org/u/rkjnsn)\
**Post date:** [April 1, 2015, 12:46am UTC](https://internals.rust-lang.org/t/pre-rfc-change-abs-to-return-unsigned-integers/1747/8 "2015-04-01T00:46:14Z")

</div>

👍 After having seen some of the problems this can cause in C, I think the proposal here is definitely the way to go. I’d say post a full RFC and hopefully it can be accepted by Friday.

---

<div class="post-metadata">

**Author:** ![pitdicker](https://avatars.discourse-cdn.com/v4/letter/p/bbe5ce/32.png) [@pitdicker](https://internals.rust-lang.org/u/pitdicker)\
**Post date:** [April 1, 2015, 6:36am UTC](https://internals.rust-lang.org/t/pre-rfc-change-abs-to-return-unsigned-integers/1747/9 "2015-04-01T06:36:03Z")

</div>

I should have posted here, I have made a pull request with a rfc:

> <https://github.com/rust-lang/rfcs/pull/1017>

---

<div class="post-metadata">

**Author:** ![system](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/system/32/14092_2.png) [@system](https://internals.rust-lang.org/u/system)\
**Post date:** [March 25, 2019, 8:10am UTC](https://internals.rust-lang.org/t/pre-rfc-change-abs-to-return-unsigned-integers/1747/10 "2019-03-25T08:10:21Z")

</div>

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