#Ops
Status: standard library specification draft.
This chapter defines the part of std.ops that lib/std writes in
ordinary hd over the language tier:
- the
Defaulttrait; - the standard
Defaultimplementations, including its tuple template; - derived
Default, through the trait's template, and the@defaultvariant marker; Eqfor the range types.
The language tier keeps the rest of std.ops, since the compiler knows
those items by name:
| Item | Why it stays in the language tier |
|---|---|
the operator traits, such as Add and Neg |
lang items that operators call (Operator Traits) |
NumSuffix, StrPrefix, Template |
the compiler recognizes them by their qualified names (Literal Suffixes) |
Range, RangeFrom, RangeTo, RangeFull |
lang items that range expressions build (Range Expressions) |
#Default Trait
Default gives a type's default value:
use std.ops.Default
fn fresh[T < Default]() -> T:
T::default()
fn start() -> (i32, string, bool):
fresh()- std-ops.default.trait
std.opsdeclarestrait Defaultwith one associated function,fn default() -> Self, which returns the type's default value. - std-ops.default.import
Defaultis not a prelude name, so code imports it, as inuse std.ops.Default. - std-ops.default.callGeneric code calls it through a bound, as in
T::default(), bytrait.assoc-call.parameter.
#Standard Implementations
std implements Default for these types:
| Rule | Type | Default value |
|---|---|---|
| std-ops.default.std.integerIntegers | i8, i16, i32, i64, u8, u16, u32, u64, and usize |
0 |
| std-ops.default.std.floatFloats | f32 and f64 |
0.0 |
| std-ops.default.std.boolBoolean | bool |
false |
| std-ops.default.std.stringString | string |
"" |
| std-ops.default.std.listList | List[T], for any T |
the empty list [] |
| std-ops.default.std.mapMap | Map[K, V], for any valid key type K and any V |
the empty map {} |
| std-ops.default.std.optionalOptional | T?, for any T |
.None |
| std-ops.default.std.tuple.any-sizeTuple | a tuple of any size, each of whose elements implements Default; () is included |
each element's default, as in (0, "", false) |
| std-ops.default.std.tuple.rest-emptyRest tuple | a tuple with a rest element List[T]... whose fixed elements implement Default |
each fixed element's default, then an empty rest |
- std-ops.default.std.tuple-template
std.opsdeclares a tuple template forDefault, which builds each element's default through the tuple'sStructure. - std-ops.default.std.rest-anyA rest tuple's
Tneed not implementDefault, since its default rest holds no items. - std-ops.default.std.missing-elementA tuple with an element that does not implement
Defaultdoes not implementDefault. Using it whereDefaultis required is an error. Error:unsatisfied-trait-bound.
use std.ops.Default
struct Secret:
value: i32
fn fresh[T < Default]() -> T:
T::default()
fn start() -> (i32, Secret):
fresh() # error: unsatisfied-trait-boundNote. The default of
(i32, string, List[i32]...)is(0, ""): each fixed element's default, then an empty rest.
Why. Generic code needs a starting value it cannot write as a literal, as
T::zero()gives numbers one. A rest tuple's rest is a list, and an empty list needs nothing from its items.
See also: Associated Function Calls, Derived Tuple Implementations.
#Derived Default
@derive(Default) builds a struct's or an enum's default from its
members' defaults. A member's declared default wins over its type's. An
enum marks its default variant with @default:
use std.ops.{Default, default}
@derive(Default)
struct Settings:
name: string
retries: i32 = 3
tags: List[string]
@derive(Default)
enum Shape:
Circle(radius: f64)
@default
Square(side: f64, label: string)
fn start() -> (Settings, Shape):
(Settings::default(), Shape::default())Settings::default() is Settings { name: "", retries: 3, tags: [] },
and Shape::default() is .Square(0.0, "").
std.ops declares the marker:
@annotate(.Variant)
pub struct DefaultVariant: pass
pub fn default() -> DefaultVariant- std-ops.default.derive.template
std.opsdeclares the template ofDefault, which@derive(Default)instantiates. It is ordinarystd.opscode overstd.structure, as the templates ofEqandHashare. - std-ops.default.derive.data.declaredA derived struct's default sets a member that declares a default, as
retries: i32 = 3, to that default. - std-ops.default.derive.data.type-defaultIt sets every other member to its own type's default,
F::default(). - std-ops.default.derive.member-bound.undeclaredThe template requires
Defaultonly of the type of a member that declares no default. Such a member whose type does not implement it makes the type not derivable, reported at the opt-in and naming the member. Error:member-not-derivable. - std-ops.default.derive.member-bound.declaredA member that declares a default needs no
Defaulton its type, assecret: Secret = Secret { value: 7 }shows below. - std-ops.default.derive.source-factThe template's source implementation carries
fills_defaults(). Sobuildfills a declared default itself, byannot.impl-fact.fills-defaults, and the member is never offered to the source. - std-ops.default.derive.marker
std.opsdeclares the fact typeDefaultVariantand the functiondefault. Code importsdefault, as inuse std.ops.default, and writes@defaulton a variant, which attachesdefault(). - std-ops.default.derive.enumA derived enum's default is its variant marked
@default. - std-ops.default.derive.payloadThe default of a marked payload variant fills each payload member with its own type's default.
- std-ops.default.derive.one-variantA derived enum must mark exactly one variant
@default. An enum with no marked variant is an error, reported on the@deriveline. Error:invalid-default-variant. - std-ops.default.derive.one-variant.severalAn enum with several marked variants is the same error, reported on the second
@default. Error:invalid-default-variant.
use std.ops.{Default, default}
@derive(Default) # error: invalid-default-variant
enum Mode:
Fast
Slow
@derive(Default)
enum Level:
@default
Low
@default # error: invalid-default-variant
Highuse std.ops.Default
struct Secret:
value: i32
@derive(Default) # error: member-not-derivable
struct Vault:
secret: Secretuse std.ops.Default
struct Secret:
value: i32
@derive(Default)
struct Safe:
secret: Secret = Secret { value: 7 }
count: i32
fn opened() -> i32:
Safe::default().secret.valueNote. A generic type gets
T < Defaultfor each type parameter that a built member uses, byannot.bound.params. A member that a derivation block omits takes its declared default, byannot.omit.default.
Why. No variant is more neutral than another, so the enum names its default, as Rust's
#[default]does. A struct has one shape, so its default needs no marker.
See also: Derived Implementations, Templates.
#Range Equality
The range types compare field by field:
use std.ops.Range
fn same_span(left: Range[i32], right: Range[i32]) -> bool:
left == right| Rule | Type | Equal when |
|---|---|---|
| std-ops.range.eq.rangeRange | Range[T], where T < Eq |
start, end, and inclusive are each equal |
| std-ops.range.eq.fromFrom | RangeFrom[T], where T < Eq |
start is equal |
| std-ops.range.eq.toTo | RangeTo[T], where T < Eq |
end and inclusive are each equal |
| std-ops.range.eq.fullFull | RangeFull |
always |
- std-ops.range.eq.inclusiveSo
0..3and0..=2are not equal, though they hold the same integers.
Why. A range is a plain value, and std value types implement
Eq. Comparing the fields, not the integers, is what Rust's derivedPartialEqforRangedoes.
See also: Range Expressions.