Hi! I wrote my very first RFC today! I’m posting it here on internals first to answer two questions:

- Are these additions too small to write an RFC for?
- Is the language here good enough for an RFC?

I think I’ve formulated this as an RFC because I’m more comfortable drafting text than submitting a PR to the compiler. So ummm, I hope this is good! Keen to receive feedback!

- Feature Name: leading_ones_trailing_ones
- Start Date: 2018-11-04
- RFC PR: (leave this empty)
- Rust Issue: (leave this empty)

# Summary

We propose adding the `.leading_ones()`

, and `.trailing_ones()`

methods to
numbers as counterparts to `.leading_zeros()`

, and `.trailing_zeros()`

.

# Motivation

## Symmetry

In Rust there are currently 4 operations that count ones and zeros inside the binary representation of numbers:

`<num>::count_ones()`

`<num>::count_zeros()`

`<num>::leading_zeros()`

`<num>::trailing_zeros()`

`count_zeros()`

has a counterpart in `count_ones()`

, but all other methods
do not have any counterparts.

In the Rust ecosystem it’s easy to get used to the symmetry in methods. For
example: `Option::is_none()`

is balanced with `Option::is_some()`

, and
`<num>::rotate_left()`

is balanced with `<num>::rotate_right()`

.

Adding counterparts to `.leading_zeros()`

and `.trailing_zeros()`

would create
symmetry in those APIs, which seems like a natural fit for Rust’s stdlib.

## Expressiveness & Reducing Errors

To count the number of trailing ones in a number, you’d currently have to write something similar to:

```
fn do_something(i: u8) {
let x = (!i).count_zeros();
// (...)
}
```

This is not ideal for a few reasons. The binary negation operator (`!`

) might
not be familiar for a lot of people. In languages such as JavaScript it would
evaluate to a boolean, and for people coming from those languages it might be a
little confusing at first sight what’s going on.

Also there’s some operator precedence to learn here: the negation operator is evaluated after the method call. This requires the addition of braces to ensure the statements are evaluated in the right order.

Instead this would be more expressive, and less prone to errors:

```
fn do_something(num: u8) {
let x = i.count_ones();
// (...)
}
```

# Guide-level explanation

`<num>::trailing_ones()`

The `.trailing_ones()`

method would return the number of trailing ones in the
binary representation of the number.

```
let n = 0b1001011u8;
assert_eq!(n.trailing_ones(), 2);
```

This method would be available on all numerical types.

`<num>::leading_ones()`

The `.leading_ones()`

method would return the number of leading ones in the
binary representation of the number.

```
let n = 0b1001011u8;
assert_eq!(n.leading_ones(), 1);
```

This method would be available on all numerical types.

# Reference-level explanation

`<num>::trailing_ones()`

The `.trailing_ones()`

method would be implemented as part of
`libcore/num/mod.rs`

,
and added to each numerical type:

```
#[rustc_const_unstable(feature = "const_int_ops")]
#[inline]
pub const fn trailing_ones(self) -> u32 {
(!self).trailing_zeros()
}
```

This implementation would be similar to
`count_zeros()`

.

The difference in (optimized) x86 assembly would be a single instruction:

```
trailing_zeros:
movzx eax, dil
or eax, 256
tzcnt eax, eax
ret
trailing_ones:
not dil ; added
movzx eax, dil
or eax, 256
tzcnt eax, eax
ret
```

`<num>::leading_ones()`

The `.leading_ones()`

method would be implemented as part of
`libcore/num/mod.rs`

,
and added to each numerical type:

```
#[rustc_const_unstable(feature = "const_int_ops")]
#[inline]
pub const fn leading_ones(self) -> u32 {
(!self).leading_zeros()
}
```

This implementation would be similar to
`count_zeros()`

.

The difference in (optimized) x86 assembly would be a single instruction:

```
leading_zeros:
movzx eax, dil
lzcnt eax, eax
add eax, -24
movzx eax, al
ret
leading_ones:
not dil ; added
movzx eax, dil
lzcnt eax, eax
add eax, -24
movzx eax, al
ret
```

# Drawbacks

## API Surface Growth

Adding this feature would increase the overall API of Rust by introducing two new methods to every number type. However this increase only adds symmetrical counterparts to existing APIs, so there should be a minimal amount of extra learning needed.

## No Direct Mapping To Intrinsics

The addition of these methods doesn’t have a direct mapping to any Rust intrinsics, and/or LLVM intrinsics.

For `.trailing_zeros`

there’s the
`cttz intrinsic`

, which
in LLVM maps onto
`llvm.cttz`

. A similar
mapping exists in `.leading_zeros()`

through `ctlz`

. This mapping would not
exist for the two APIs we’re proposing in this RFC.

However there is prior art for a non-direct mapping in `.count_zeros()`

and
`.count_ones()`

. `.count_zeros()`

is implemented as a negation of
`.count_ones()`

,
which would be similar to what we’re proposing:

```
pub const fn count_zeros(self) -> u32 {
(!self).count_ones()
}
```

# Rationale and alternatives

## Do Nothing

This RFC doesn’t introduce anything that wasn’t possible before, but instead intends to codeify patterns into methods. This lowers the bar for accessing this functionality, and provides a more streamlined experience.

## Introduce As A `NumExt`

library

It might be argued that convenience methods should live in user land, as part of libraries that provide extension traits to stdlib.

However, that would seem like it would have the same discovery problems as
learning the `(!<num>).<method>`

pattern, and could arguably be considered too
small of a library which probably would see little usage. Making it effectively
the same as doing nothing.

## Naming

Arguably using the `*_ones()`

suffix might lead to confusing. When reading
`.trailing_ones()`

in code without context, it might be reasonable to ask:
“Which ones are trailing?”, and “Do they feel lonely?”.

An alternative naming scheme might include a `*_1s()`

suffix, removing some of
the ambiguity. However this would break the precedent set in methods such as
`.count_ones()`

. Therefor it seems best to follow the existing scheme of pairing
`zeros`

with `ones`

.

# Prior art

## Rust’s Stdlib

Rust’s `<num>::count_zeros()`

method is implemented as a binary negation of
`<num>::count_ones()`

, which maps to the `popcnt`

instruction.

In x86 assembly, the difference between the two
methods is a single `not`

instruction:

```
count_ones:
movzx eax, dil
popcnt eax, eax
ret
```

```
count_zeros:
not dil ; added
movzx eax, dil
popcnt eax, eax
ret
```

For the methods we’re proposing, the difference would be similar.

## C++

We’re currently not aware of any prior art for this method in other languages.
Most of the prior art we’ve found has been through access to compiler intrinsics
such as gcc’s
`__builtin_clz`

and
msvc’s `popcnt`

.

A quick search on the internet brings up examples to manually combine these methods such as:

```
#ifdef __GNUG__
int count_ones (unsigned int a) {
return __builtin_popcount(a) ;
}
#endif // __GNUG__
#ifdef _MSC_VER
int count_ones (unsigned int a) {
return __popcount(a);
}
#endif // _MSC_VER
```

This seems to have a reasonable amount of boilerplate, and is less convenient than having a single, portable method available.

## Dat Protocol

The Dat protocol makes use of a data structure called “Flat in-order trees”, where entries have a “depth” property that can be calculated by counting the amount of trailing ones in the binary representation of numbers.

```
0─┐
1─┐
2─┘ │
3
4─┐ │
5─┘
6─┘
5 in binary = 101 (one trailing 1)
3 in binary = 011 (two trailing 1s)
4 in binary = 100 (zero trailing 1s)
```

It seems reasonable to assume that more algorithms exist that make use of similar number properties.

# Unresolved questions

None.

# Future possibilities

While researching how to most efficiently count trailing ones, we found some interesting operations that currently don’t seem to be exposed in Rust.

For example Intel’s
`bit_scan_*`

range of instructions might be interesting to add.

There’s probably a balance to be had, as there’s probably such a thing as “too many methods available”. But it might still be interesting for Rust to look at which other methods could be exposed, as it could enable some interesting optimizations for algorithms that might otherwise require some guesswork to achieve.

This would be similar to how `SIMD`

intrinsics have recently be introduced, as a
no-guesswork counterpart to auto-vectorization.