Skip to content
academia.sh

Course Intermediate

Traits, Generics and Error Handling

Published Updated

By the end of this course

Start course

01

Traits and Generics

A behavior contract defined independent of type, generic code stating its requirement with a bound, a contract carrying a type within itself, choosing whether a call is resolved at compile time or at run time, unwritten bodies produced through deriving, and operators gaining behavior through a contract.

  1. 01 Trait Definition and Implementation A trait establishes a behavior contract independent of type and can carry a default method body; when a sixteen-body measurement kernel runs, this default body is seen to run four times for one type and zero times for a type that overrides it — the question asked throughout the course is this: how many times did this body you wrote in the source run?
  2. 02 Trait Bounds Putting a trait bound on a generic function's type parameter adds no step to the source at all — the same body written and run in both a bounded and an unbounded form gives back the exact same number in both; the bound's cost is written to the compilation, not to the run ledger, and it cannot be measured by this number.
  3. 03 Associated Types A trait can carry a type within itself without taking a type parameter — an associated type; when the same contract is written with an associated type and with a generic parameter, the run count comes out exactly the same, the difference is in what the caller has to write in the signature, and an associated type carries a constraint that allows only one implementation per type.
  4. 04 Object Safety and Dynamic Dispatch A call written with a trait bound resolves at compile time; a call written through a trait object (dyn) resolves at run time — in the measurement kernel both run twice; the difference is not in the count, it is in how the call is found, and this is only possible for traits that satisfy the object safety condition.
  5. 05 Derived Traits An attribute line can implement a trait without hand-writing it — derive; a hand-written body can place a counter call into the run ledger, while a derived body has no source line to count it in, so it can only be observed by its behavior — and the same derive gives two different results on two different data layouts.
  6. 06 Operator Overloading Every use of an operator (like +) is a trait call hiding behind it; a three-term sum calls it twice, a five-term sum four times — the operator's short syntax does not change the call count, it only changes whether the call is visible in the source, and this number can only be known from the run ledger.

02

Error Handling

Expressing absence in a type, carrying failure as a return value, shortening error propagation to a single mark, converting and chaining errors into their own types, and placing the unrecoverable case in a separate spot in the design.

  1. 01 Expressing Absence with a Type Carrying the possibility that a value is absent in a type, and measuring whether the fallback body that covers that value — depending on whether it is written eagerly or lazily — runs even while the value already exists.
  2. 02 Failure as a Return Value Writing an operation's success and failure paths as separate bodies in the source, and measuring how the run counts of these two arms diverge while the total call count stays fixed as the same function is called repeatedly.
  3. 03 The Shorthand for Error Propagation Measuring that the shorthand which reduces checking an error and returning early to a single mark gives exactly the same counts as the same job written by hand with a branch — that it shortens the source without changing the run.
  4. 04 Custom Error Types Measuring that the question mark converts one error type into another without the caller writing any conversion call, and that this implicit conversion — unlike a hand-written one — cannot carry context information from the caller.
  5. 05 Panic and Unrecoverable Errors Modeling side by side a design where a failure stops the remaining body without leaving the caller any choice, and a design where the same failure is met with a return value, and comparing the counts the two designs give for the remaining body.

03

Closures and Iterators

A body capturing its surroundings and being deferred to later, traversal being reduced to a single contract, chained transformations waiting until they are consumed, and conversion to data formats being done with bodies that run per field.

  1. 01 Closures A closure's body does not run on the line where it is defined but at the moment it is called: the same job is written as a direct call, a deferred call, and a closure that is never called, and the run counts are compared; what moving versus capturing by reference does to the source value is also measured.
  2. 02 The Iterator Trait Traversing a source is reduced to a single contract — a single call asking for the next value in sequence — and that contract only advances when asked: a chain that is built but never consumed never runs its body at all.
  3. 03 Iterator Adapters Two chains starting from the same four-element source have source text that is almost identical, yet one runs its body on every element while the other only as much as needed: what determines the count is not the chain's length but the consumer bound to its end, and chained adapters' bodies run in per-element order.
  4. 04 Serialization How many times a hand-written transformation body applied to a struct's fields runs per field, how that count grows multiplicatively in a nested struct, and how, in the reverse direction — from text back to a struct — one field's failure never runs the following fields' bodies at all; every body measured across the course's fifteen lessons is gathered here into a single table.

Final exam

Traits, Generics and Error Handling — Final Exam

In preparation

This course's question bank is not complete yet. The exam opens here automatically once enough questions are added.

Start typing to search.

↑↓ ↵ Esc navigate · open · close