#Cmp
Status: standard library specification draft.
This chapter defines the part of std.cmp that lib/std writes in
ordinary hd over the language tier:
- what a derived
Eq,PartialOrd, orOrdcompares, through the traits' templates; - how tuples compare, through the traits' tuple templates;
- the functions
min,max, andclamp; - the
Reverse[T]wrapper; - the traits that
Orderingimplements; - equality of references,
Id[T], which compares references by place, anddeep_equal, which ends on cyclic structures.
The language tier keeps what the compiler knows by name (Comparison Traits):
| Item | Why it stays in the language tier |
|---|---|
Eq, PartialOrd, Ord, Ordering |
lang items that == and the relational operators call |
@derive and its checks |
derive-field-missing-trait, missing-derived-bound, and mixed-derived-law are compiler diagnostics (Derived Implementations, Related Traits) |
| templates and tuple templates | Typed Derivation |
#Derived Equality
@derive(Eq) compares two values member by member:
@derive(Eq)
enum State:
Active(code: i32, label: string)
Paused(code: i32)
fn same(a: State, b: State) -> bool:
a == b- std-cmp.derive.eq.template
std.cmpdeclares the template ofEq, which@derive(Eq)instantiates. - std-cmp.derive.eq.compare-fieldsDerived
Eqcompares every declared struct field, including embedded fields, by itsEqimplementation. - std-cmp.derive.eq.no-exclusionNo field is implicitly excluded.
- std-cmp.derive.eq.enumDerived enum equality first compares the variant, then every payload field of that variant, including common enum fields.
- std-cmp.derive.eq.variantsDifferent variants are unequal.
- std-cmp.derive.eq.eqDerived
Eqrequires every compared field to satisfyEq. - std-cmp.derive.eq.cyclesDerived equality does not detect cycles or track previously compared objects: it recursively invokes each field's
Eqimplementation. - std-cmp.derive.eq.stackA comparison that repeatedly traverses a cycle may exhaust the execution stack.
- std-cmp.derive.eq.referencesA reference field compares by the values the two references refer to, through Reference Equality. So two trees built alike through references are equal.
#Derived Ordering
@derive(PartialOrd, Ord) orders values member by member:
@derive(Eq, PartialOrd, Ord)
struct Key:
first: i32
second: i32
fn before(a: Key, b: Key) -> bool:
a < b- std-cmp.derive.ord.template
std.cmpdeclares the templates ofPartialOrdandOrd, which@deriveinstantiates. - std-cmp.derive.ord.support
@derive(PartialOrd, Ord)supports structs and enums. - std-cmp.derive.ord.dataDerived ordering is lexicographic in declared struct-field order, including embedded fields.
- std-cmp.derive.ord.variantsFor enums, distinct variants compare by variant declaration order.
- std-cmp.derive.ord.same-variantValues of the same variant compare shared enum data in declaration order, followed by that variant's payload parameters in declaration order.
- std-cmp.derive.ord.argument-orderConstructor argument order does not affect comparison.
- std-cmp.derive.ord.partialDerived
PartialOrdrequires every compared field to satisfyPartialOrd. It returns.Noneif a field comparison is unordered before a comparison result is determined. - std-cmp.derive.ord.ordDerived
Ordrequires every compared field to satisfyOrd.
#Tuple Comparison
Tuples compare element by element, at every size:
fn compare(a: (i32, string), b: (i32, string)) -> bool:
a == b || a < b- std-cmp.tuple.templates
std.cmpdeclares tuple templates forEq,PartialOrd, andOrd. A tuple implements each when every element does. - std-cmp.tuple.elementwiseEquality compares every element, in order. Ordering is lexicographic.
- std-cmp.tuple.partialTuple
PartialOrdreturns.Nonewhen an element comparison is unordered before a result is determined. - std-cmp.tuple.restA tuple's rest element
List[T]...is its last element, of typeList[T], so it compares as that list.
Note. A 13-element tuple compares as a 2-element one does: no tuple template stops at a size.
See also: Derived Tuple Implementations, Tuple Structure, Hash.
#Min And Max
min and max pick one of two values:
use std.cmp.{max, min}
fn spread(a: i32, b: i32) -> i32:
max(a, b) - min(a, b)- std-cmp.min-max.decl
std.cmpdeclarespub fn min[T < Ord](a: T, b: T) -> Tandpub fn max[T < Ord](a: T, b: T) -> T. Code imports them, as inuse std.cmp.{max, min}. - std-cmp.min-max.result
minreturns the smaller ofaandb, andmaxthe larger, bycmp. - std-cmp.min-max.tieWhen
a.cmp(b)is.Equal, bothminandmaxreturna. - std-cmp.min-max.not-preludeNeither name is in the prelude.
Why. One first-among-equals rule serves
min,max,List.min, andList.max, as in Python. Rust'smaxreturnsbon a tie.
#Clamp
clamp limits a value to a range:
use std.cmp.clamp
fn percent(value: i32) -> i32:
clamp(value, 0, 100)- std-cmp.clamp.decl
std.cmpdeclarespub fn clamp[T < Ord](value: T, low: T, high: T) -> T. Code imports it, as inuse std.cmp.clamp. - std-cmp.clamp.result
clampreturnslowwhenvalueis belowlow,highwhenvalueis abovehigh, andvalueotherwise. - std-cmp.clamp.orderA
lowabovehighpanics. Panic:explicit-panic.
#Reverse
Reverse[T] wraps a value and inverts its order, as Rust's
std::cmp::Reverse does:
use std.cmp.Reverse
fn later_first(a: i32, b: i32) -> Ordering:
Reverse { value: a }.cmp(Reverse { value: b }) # .Less when a > b- std-cmp.reverse.decl-struct
std.cmpdeclarespub struct Reverse[T]with one public field,value: T. It is a value: a copy of aReverseholds a copy of the wrapped value, and a change to one is not seen through the other. Code imports it, as inuse std.cmp.Reverse. - std-cmp.reverse.orderWhen
T < Ord,Reverse[T]implementsEq,PartialOrd, andOrd, andReverse { value: a }.cmp(Reverse { value: b })isb.cmp(a). - std-cmp.reverse.eqTwo
Reversevalues are equal exactly whencmpof their values gives.Equal.
#Ordering Values
Ordering is a lang item; std.cmp gives it its traits:
fn is_before(a: string, b: string) -> bool:
a.cmp(b) == Ordering.Less- std-cmp.ordering.eq
OrderingimplementsEq. Two values are equal exactly when they are the same case. - std-cmp.ordering.debug-text
OrderingimplementsDebug, and writes its qualified case:Ordering.Less,Ordering.Equal, orOrdering.Greater. - std-cmp.ordering.no-display
Orderingdoes not implementDisplay;debug(o)gives its text. - std-cmp.ordering.then.decl
std.cmpdeclares the inherent methodpub fn then(self, other: Ordering) -> OrderingonOrdering, so everyOrderinghas it without ause, bytrait.own.inherent.std.no-use. - std-cmp.ordering.then.result
thenreturnsselfunlessselfis.Equal, and returnsotherfor.Equal..Less.then(o)and.Greater.then(o)are.Lessand.Greater;.Equal.then(o)iso. - std-cmp.ordering.then.chain
thenchains the keys of a comparator:b.score.cmp(a.score).then(a.name.cmp(b.name))orders by score, highest first, and by name among equal scores. - std-cmp.ordering.then.eager
otheris an ordinary argument, evaluated before the call, whateverselfis.thendoes not suspend.
#Reference Equality
std.cmp implements Eq for references by the values they refer to:
fn has_value(items: List[*i32], value: *i32) -> bool:
items.contains(value) # compares the values the references refer to- std-cmp.ref.eq
std.cmpimplementsEqfor&Tand for&mut TwhenT < Eq. Two references are equal when the values they refer to are, sor.eq(s)is*r == *s. - std-cmp.ref.eq-operator
==written between two references stays an error, byref.identity.no-equality. Generic code and derived implementations reach this implementation.
Why. A reference is equal by value, as in Rust, so a derived
Eqof an expression tree compares its shape.iskeeps identity for code that needs it.
#Identity Keys
Id[T] wraps a reference and compares it by the place it refers to, so
a map keyed by Id[T] keys by identity:
use std.cmp.Id
struct Node:
label: string
fn visit(seen: &mut Map[Id[Node], bool], node: *Node) -> bool:
key := Id { target: node }
if seen.get(key).is_some():
return false
seen[key] = true
true- std-cmp.id.decl
std.cmpdeclarespub struct Id[T]with one public field,target: &T. Code imports it, as inuse std.cmp.Id. - std-cmp.id.eq
Id[T]implementsEqfor everyT. Two ids are equal exactly when their targets refer to the same place, asisdecides. - std-cmp.id.hash
Id[T]implementsHashfor everyT, so it may be a map key. Two equal ids hash alike; the bytes written are unspecified.
#Deep Equality
deep_equal compares two values member by member, through references,
and ends on cyclic structures:
use std.cmp.{DeepEq, deep_equal}
@derive(DeepEq)
struct Node:
value: i32
next: *mut Node?
fn same_ring(a: &mut Node, b: &mut Node) -> bool:
deep_equal(a, b)- std-cmp.deep-equal.decl
std.cmpdeclarespub fn deep_equal[T < DeepEq](left: T, right: T) -> bool. Code imports it, as inuse std.cmp.{DeepEq, deep_equal}. - std-cmp.deep-equal.trait-ref
std.cmpdeclarespub trait DeepEqwith the one methodfn deep_eq(self, other: Self, visits: &mut DeepVisits) -> bool. - std-cmp.deep-equal.visits-ref
std.cmpdeclarespub struct DeepVisitswith private fields. It holds the pairs of references that onedeep_equalcall is comparing.deep_equalstarts with none and passes the one record to everydeep_eqof the call as&mut DeepVisits, so a pair remembered at any depth is seen at every other. - std-cmp.deep-equal.template
std.cmpdeclares the template ofDeepEq, which@derive(DeepEq)instantiates, and a tuple template. - std-cmp.deep-equal.deriveA derived
DeepEqcompares the variant, then every member in declaration order by itsDeepEq, as a derivedEqdoes. - std-cmp.deep-equal.leavesThe number types,
char,bool, andstringimplementDeepEqby==. - std-cmp.deep-equal.containersOptionals, results, lists, and tuples implement
DeepEqwhen their elements do, and compare element by element.Map[K, V]implements it whenK < Eq & HashandV < DeepEq: both maps have the same keys, and the values under each key are deeply equal. - std-cmp.deep-equal.references
&Tand&mut TimplementDeepEqwhenT < DeepEq. Two references to one place are deeply equal. Otherwise a pair the call is already comparing counts as equal, and a new pair is remembered before the values it refers to are compared. - std-cmp.deep-equal.endsSo
deep_equalends on every structure, cyclic ones included, as Go'sreflect.DeepEqualdoes.
Why. A derived
Eqstays as light as a field-by-field comparison, so it keeps no record of the pairs it has compared and runs around a cycle until the stack is exhausted.deep_equalis the cycle-aware comparison for the structures that need one.
Why.
deep_equalreaches members through aStructuretemplate, as the derivedEqandDebugdo. hd has no runtime reflection, andStructureexists only inside a template (annot.structure.generated). So a derived trait is how a std function reaches every member of a user type. The derivedEqcannot serve: its walker calls==, which carries no record of the pairs being compared.