Skip to content
CryoCryo home
Stdlibcore

ops

import std::core::ops; — in the prelude · source

Range<T>

type struct Range<T> {
    start: T;
    end:   T;

    static new(start: T, end: T) -> Range<T>;
}

a..b desugars to Range::new(a, b) and is half-open: it yields start, then every successor below end.

for (i in 0..5) { println(f"{i}"); }   // 0 1 2 3 4

In pattern position a..b is inclusive. In expression position it is half-open. See Pattern Matching.

RangeInclusive<T>

type struct RangeInclusive<T> {
    start:     T;
    end:       T;
    exhausted: boolean;

    static new(start: T, end: T) -> RangeInclusive<T>;
}

The closed form, a..=b. The exhausted flag is what lets it yield end itself and then stop, without needing a value past end to exist.

Trait implementations

implement<T> trait Iterator<T> for struct Range<T>
where T: Step

implement<T> trait Iterator<T> for struct RangeInclusive<T>
where T: Step

Step

type trait Step : Ord {
    successor(&this) -> This;
}

Step is what makes one generic Range<T> work across widths — it supplies successor, and it requires Ord because bounded iteration has to know when it has passed end. It is implemented for every fixed-width integer up to 64 bits, and for char.

Implementors

implement trait Step for i8

implement trait Step for i16

implement trait Step for i32

implement trait Step for i64

implement trait Step for u8

implement trait Step for u16

implement trait Step for u32

implement trait Step for u64

implement trait Step for char

Operator traits

type trait Add<Rhs, Output> {
    add(&this, rhs: &Rhs) -> Output;
}

type trait Sub<Rhs, Output> {
    sub(&this, rhs: &Rhs) -> Output;
}

type trait Mul<Rhs, Output> {
    mul(&this, rhs: &Rhs) -> Output;
}

type trait Div<Rhs, Output> {
    div(&this, rhs: &Rhs) -> Output;
}

type trait Rem<Rhs, Output> {
    rem(&this, rhs: &Rhs) -> Output;
}

type trait Neg<Output> {
    neg(&this) -> Output;
}

type trait BitAnd<Rhs, Output> {
    bitand(&this, rhs: &Rhs) -> Output;
}

type trait BitOr<Rhs, Output> {
    bitor(&this, rhs: &Rhs) -> Output;
}

type trait BitXor<Rhs, Output> {
    bitxor(&this, rhs: &Rhs) -> Output;
}

type trait Shl<Rhs, Output> {
    shl(&this, rhs: &Rhs) -> Output;
}

type trait Shr<Rhs, Output> {
    shr(&this, rhs: &Rhs) -> Output;
}

type trait Not<Output> {
    not(&this) -> Output;
}

type trait BitNot<Output> {
    bitnot(&this) -> Output;
}

type trait Index<Idx, Output> {
    index(&this, idx: Idx) -> Output*;
}

type trait Deref<Target> {
    deref(&this) -> Target*;
}

Implementing one of these lets the compiler rewrite the corresponding operator on your type. The rewrite is type-directed and left-hand-driven — a OP b dispatches on a's type — and it only fires when the primitive rules do not apply, so 1 + 2 and pointer arithmetic stay on the native path. Primitives deliberately do not implement these traits.

TraitOperator
Add Sub Mul Div Rem+ - * / %
Negunary -
BitAnd BitOr BitXor Shl Shr& | ^ << >>
Not / BitNot! / ~
Index<Idx, Output>a[i]
Deref<Target>*b, and auto-deref on b.field / b.method()

The right operand is taken by reference so an owned aggregate is not moved, and Rhs/Output are separate type parameters so a type can (say) add a scalar to a vector and yield a vector. Compound assignment routes through the same trait: a += b evaluates a.add(&b) and stores the result back.

Index and Deref both return a pointer, which makes the single impl serve reads, writes, and compound assignment alike — v = a[i], a[i] = v, and a[i] += v all go through index. There is no separate DerefMut, because Cryo pointers are not const-qualified. The one Deref impl in the library is Box<T>.