side = "6"radius = "3"// TOUR: generics and traits//// The subject is what the debugger shows you when the source is written once// and executed several times over different types. A generic function has one// body and many instantiations; a trait method has one name and many bodies.// Both are places where "which code is running" is a real question, and the// call trace is where it gets answered.mod shapes;use shapes::{Circle, Rect, Shape};// A type generic. One body, three instantiations — `u32`, `u64` and `i32` —// and the call trace names the frame each time it is entered.//// The bound is `Ord`, which `Field` deliberately does NOT implement: field// elements wrap, so "greater than" is not a question they answer. That refusal// is a language fact worth meeting here rather than in a compiler error later.unconstrained fn largest<T>(a: T, b: T) -> Twhere T: std::cmp::Ord,{ if a > b { a } else { b }}// A NUMERIC generic: `N` is a value, not a type, and the same source runs over// arrays of two different lengths. The loop bound is the generic, so the loop// rail has a different number of rungs per instantiation.unconstrained fn sum_all<let N: u32>(values: [u32; N]) -> u32 { let mut total: u32 = 0; for i in 0..N { total = total + values[i]; } total}// A generic constrained by a trait this package defines. Which `area` runs is// decided by the argument's type, and the only place a reader can SEE which// one ran is the trace.unconstrained fn describe<T>(shape: T) -> u32where T: Shape,{ let a = shape.area(); let p = shape.perimeter(); a + p}unconstrained fn main(side: u32, radius: pub u32) -> pub u32 { // Three instantiations of one generic function, over three types. let big_u = largest(side, 17); let big_w = largest(side as u64, 4_000_000_000); // The suffix is load-bearing: a bare negative literal is a `Field`, and // `Field` has no `Ord`. let big_i = largest(-3_i32, -9_i32); // Two instantiations of a numeric generic, N = 3 and N = 5. let three = sum_all([1, 2, 3]); let five = sum_all([side, side, side, side, side]); // Two impls of one trait, dispatched by type. let c = describe(Circle { radius }); let r = describe(Rect { w: side, h: side + 1 }); assert(big_u >= 17); assert(three == 6); assert(big_i == -3); assert(big_w == 4_000_000_000); big_u + three + five + c + r + big_w as u32}// A trait with two implementations, in a module of its own so that stepping// into a trait method crosses a file boundary and the call trace has to name// which impl it entered — the thing a source view alone cannot tell you.pub trait Shape { fn area(self) -> u32; // A DEFAULT method: `Circle` takes it as written and `Rect` overrides it, // so the same call spelling reaches two different bodies and the trace is // what distinguishes them. fn perimeter(self) -> u32 { self.area() * 2 }}pub struct Circle { pub radius: u32,}pub struct Rect { pub w: u32, pub h: u32,}impl Shape for Circle { fn area(self) -> u32 { // 3 is a deliberately crude pi: this program is about dispatch, and a // value a reader can verify in their head is worth more here than a // correct one they cannot. 3 * self.radius * self.radius } // `perimeter` is NOT overridden — the default body runs.}impl Shape for Rect { fn area(self) -> u32 { self.w * self.h } fn perimeter(self) -> u32 { 2 * (self.w + self.h) }}[package]name = "tour_generics"type = "bin"authors = ["BlockTracer capability tour"][dependencies]The recorded event stream is in the published recording. Reading it needs the replay engine, which this page has not started.
The call structure is in the published recording. Reading it needs the replay engine, which this page has not started.
The recorded values are in the published recording. Reading them needs the replay engine, which this page has not started.