Skip to content

std.ordering

std.ordering defines the three-way Ordering vocabulary and small explicit comparators for primitive values. It defines no nominal ordering capability: user-defined order is ordinary callable code passed explicitly to the operation that needs it.

Quick Example

Zynx
import std.ordering;
import std.ordering.{ Ordering };

fn main() {
	assert ordering.compare_i32(3, 5) == Ordering.Less;
	assert ordering.compare_i32(5, 3) == Ordering.Greater;
	assert ordering.compare_i32(3, 3) == Ordering.Equal;

	assert ordering.reverse(Ordering.Less) == Ordering.Greater;
	assert ordering.compare_str("apple", "apply") == Ordering.Less;
}

Ordering

Zynx
export enum Ordering {
	Less,
	Equal,
	Greater,
}

Ordering is a payload-less Copy value. reverse exchanges Less and Greater and leaves Equal unchanged.

Equal is relative to the selected comparator. A comparator over records may compare only one key, making distinct records comparator-equivalent. It does not silently define or invoke the language's == operation.

Explicit User-Defined Order

A user-defined order is a concrete named function or closure, not an implementation of Comparable<T> or another nominal interface:

Zynx
import std.slice;
import std.ordering;
import std.ordering.{ Ordering };
import std.mem.{ AllocError };

struct Item: Copy {
	let priority: i32,
	let sequence: usize,
}

fn main() throws(AllocError) {
	let by_priority = [] (left: Item, right: Item) -> Ordering {
		return ordering.compare_i32(left.priority, right.priority);
	};

	let items: ^[Item] = try ^[
		Item { priority: 2, sequence: 0 },
		Item { priority: 1, sequence: 1 },
	];
	let sorted = slice.sort(<-items, &by_priority);

	assert sorted[0].sequence == 1 as usize;
}

The concrete callable type is preserved and borrowed. std.slice does not erase it to a universal callback, allocate an environment, or reinterpret element pointers. Descending order is equally explicit:

Zynx
let descending = [] (left: i32, right: i32) -> Ordering {
	return ordering.reverse(ordering.compare_i32(left, right));
};

There is no implicit natural-order sorting, operator-driven discovery, Comparable<T>, Ord, or general Eq interface.

Integer And Text Comparators

The integer comparator family uses ordinary mathematical ascending order:

text
compare_i8     compare_u8
compare_i16    compare_u16
compare_i32    compare_u32
compare_i64    compare_u64
compare_i128   compare_u128
compare_isize  compare_usize

compare_str compares strict UTF-8 text lexicographically by its bytes. When one string is a prefix of the other, the shorter string sorts first. Byte order is not locale collation, case folding, normalization, or grapheme order; those policies do not belong in the primitive comparator.

Each comparator is allocation-free, nonthrowing, and takes only shared bare operands.

Total Floating-Point Comparators

The only standard floating-point comparators are:

Zynx
compare_total_f32(left: f32, right: f32) -> Ordering
compare_total_f64(left: f64, right: f64) -> Ordering

They implement one target-independent, bit-deterministic refinement of the IEEE 754 totalOrder relation rather than trying to derive an order from <, >, and ==. If bits is the unsigned representation and SIGN_BIT is its high bit, comparison uses this unsigned key:

text
sign bit set     -> bitwise_not(bits)
sign bit clear   -> bits | SIGN_BIT

The result orders every floating representation, including:

  • negative and positive infinity;
  • ordinary finite values;
  • -0.0 before +0.0; and
  • negative and positive NaNs, including deterministic signaling/quiet and payload ordering.

Equal is returned exactly when the complete bit patterns are identical. The operation preserves NaN payload bits, performs no floating arithmetic, raises no signaling-NaN exception, and produces the same result on every endianness.

This is deliberately different from ordinary floating equality and relational operators. In particular, NaN is not reported Equal to every other value. Sorting or binary search over floats therefore requires an explicit comparator choice like every other element type:

Zynx
let total = [] (left: f64, right: f64) -> Ordering {
	return ordering.compare_total_f64(left, right);
};

let sorted = slice.sort(<-values, &total);

The removed compare_f32 and compare_f64 names have no compatibility alias. Their former </> implementation returned Equal for a NaN against every value and did not define transitive equivalence.

Comparator Laws

A comparator used by std.slice must define one coherent strict weak order:

  • self-comparison returns Equal;
  • reversing the arguments reverses Less and Greater;
  • strict order and comparator-equivalence are transitive; and
  • the relation does not change during an operation.

These laws permit equivalence classes larger than language equality while remaining sufficient for stable sorting and lower-bound binary search. The number and order of calls are unspecified, so callback state cannot be used to change the relation during an operation.

Use std.slice for the only standard sorting and binary-search operations. Both require an explicit concrete three-way comparator and use the same relation for ordering, equivalence, duplicate handling, and insertion points.

API Reference

Types

TypeKindDescription
OrderingenumThree-way comparison result with Less, Equal, and Greater.

Functions

FunctionSignatureDescription
reversereverse(value: Ordering) -> OrderingExchanges Less and Greater; preserves Equal.
compare_i8compare_i8(a: i8, b: i8) -> OrderingAscending order for i8.
compare_i16compare_i16(a: i16, b: i16) -> OrderingAscending order for i16.
compare_i32compare_i32(a: i32, b: i32) -> OrderingAscending order for i32.
compare_i64compare_i64(a: i64, b: i64) -> OrderingAscending order for i64.
compare_i128compare_i128(a: i128, b: i128) -> OrderingAscending order for i128.
compare_isizecompare_isize(a: isize, b: isize) -> OrderingAscending order for isize.
compare_u8compare_u8(a: u8, b: u8) -> OrderingAscending order for u8.
compare_u16compare_u16(a: u16, b: u16) -> OrderingAscending order for u16.
compare_u32compare_u32(a: u32, b: u32) -> OrderingAscending order for u32.
compare_u64compare_u64(a: u64, b: u64) -> OrderingAscending order for u64.
compare_u128compare_u128(a: u128, b: u128) -> OrderingAscending order for u128.
compare_usizecompare_usize(a: usize, b: usize) -> OrderingAscending order for usize.
compare_total_f32compare_total_f32(a: f32, b: f32) -> OrderingIEEE 754 total order for every f32 representation.
compare_total_f64compare_total_f64(a: f64, b: f64) -> OrderingIEEE 754 total order for every f64 representation.
compare_strcompare_str(a: str, b: str) -> OrderingLexicographic byte order for strict UTF-8 text.

Status

RFC 0047 defines the accepted target. The checked-in implementation remains transitional until Comparable<T> and compare_f32/compare_f64 are removed, the total float comparators are implemented, and std.slice consumes typed Ordering comparators directly. For the full exported surface, see Standard Library Inventory.

Released under the MIT License.