---
title: 'The Rust Ecosystem and Advanced Topics'
source: 'https://academia.sh/en/courses/rust-advanced'
language: en
updated: '2026-09-15T04:58:43+00:00'
license: 'CC BY-SA 4.0'
lessons: 12
outcomes:
    - 'Generates repeated code with macros'
    - "Distinguishes by measurement where the compiler's guarantee ends"
    - 'Explains the contract and the responsibility of an unsafe block'
    - 'Packages, documents and publishes a library'
---

# The Rust Ecosystem and Advanced Topics

A guarantee a language gives does not hold to the same degree everywhere: the course measures every construct with one question — where does the compiler's guarantee end here? Macros, unsafe code, the foreign function interface, different targets, testing, documentation and publishing all pass through the same frame.

## Macros and Metaprogramming

Pattern-based code generation being a slice of the language rather than a template, generation in attribute and derive form moving into a separate compilation step, and generated syntax being used to build a readable interface.

### Declarative Macros

Source: https://academia.sh/en/courses/rust-advanced/declarative-macros

The course's measurement core runs from inside the program and places twelve observations into four classes — still holds, handed to the writer, outside the language, nobody checks it; the distribution is 4/3/2/3, and this lesson deepens only the two observations that fall into the 'still holds' class: the body a macro generates passes type checking, and a name inside the macro does not clash with the caller's.

Throughout the Concurrency and Asynchronous Rust course, what a task needed in order to make
progress was measured — a channel, a lock, a wait point. Across four courses, one assumption
was never questioned: the compiler stands behind everything and checks every step. This course
opens exactly that assumption to measurement. The same question will be asked across twelve
different constructs: where does the compiler's guarantee end here?

The answer falls into one of four classes, and these four names do not change across the
course. This first lesson runs all twelve together, in a single measurement core; then it
deepens two of them — the check on the body a macro generates, and the fact that a macro's own
inner names do not clash with the caller's. The remaining ten questions fall to the course's
next eleven lessons; this lesson takes only the two that concern macros, since this topic's
subject is macros.

#### The Four Classes

| Class | Definition |
|---|---|
| still holds | The compiler keeps checking |
| handed to the writer | Checking stopped; whoever wrote the condition took it over |
| outside the language | Correctness is determined by another language, tool, or target |
| nobody checks it | No one checks it |

This four-way split should not be confused with the question asked in the Standard Library
and Streams course: there, the question was who gives the guarantee for a behavior — the
interface, the implementation, or the caller — that is, the parties to a contract. Here, the
question is where the compiler's own checking stops. Declarative macros give the clearest
example of the first of these four classes, still holds: the source a macro call produces is
indistinguishable, from the compiler's point of view, from hand-written source, and it is
checked by the same rules.

To see why this is not surprising, it helps to look at when the macro actually acts. A
declarative macro's expansion happens **before** type checking: the compiler first replaces
every macro call in the source with the text it produces, then reads the whole file as ordinary
Rust source and applies typing, borrowing rules, and name resolution in turn. This ordering
explains why the source a macro produces falls into the "still holds" class: the moment the
macro expands, it disappears, leaving only ordinary Rust source behind, and the mechanism that
checks that source is the same one that checks a hand-written file.

#### The Measurement Core

The core used throughout the rest of the course is `guarantee.rs`. It writes all twelve
observations into a single observation ledger and, at the end, prints the total for each class.
It uses no third-party crate: the foreign function interface is built with two functions the C
library already links in, and no outside package enters the build. The course's remaining
eleven lessons will each take only their own slice of this core; only this lesson shows all
twelve observations together.

**MM1.** The `observe` function and the `ledger` tuple (a `String`, `String`, `String` triple)
are the core's own design decision. What decides which class an observation falls into is the
shape of each line in the source; the `observe` call only records that decision into a table
so that all twelve observations can be compared in a single run.

```rust
// guarantee.rs — where does the compiler's guarantee end: does it still hold, was it handed
// to the writer, is it outside the language, or does nobody check it at all.

use std::ffi::CString;
use std::os::raw::{c_char, c_int};

// Two functions outside the language: the compiler never saw their bodies.
extern "C" {
    fn abs(x: c_int) -> c_int;
    fn strlen(s: *const c_char) -> usize;
}

// Declarative macro: the body it generates also passes type checking.
macro_rules! double_it {
    ($name:ident) => {
        fn $name(x: i32) -> i32 {
            x * 2
        }
    };
}

double_it!(generated_fn);

// Macro hygiene: the name inside does not clash with the caller's name.
macro_rules! uses_own_name {
    () => {{
        let x = 99;
        x
    }};
}

/// This doc line says the function returns the **largest** value.
/// The function returns the smallest one. No tool checks this contradiction.
fn wrong_doc(a: i32, b: i32) -> i32 {
    if a < b {
        a
    } else {
        b
    }
}

// Body excluded by cfg: the compiler never sees it at all.
#[cfg(feature = "unopened")]
fn never_checked() {
    this_function_was_never_defined();
    let _x: i32 = "text";
}

const VERSION_STRING: &str = "0.1.0";

// Function whose contract is handed to the writer: the caller guarantees the index is valid.
unsafe fn get_second_item(v: &[i32]) -> i32 {
    *v.get_unchecked(1)
}

fn observe(ledger: &mut Vec<(String, String, String)>, name: &str, class: &str, evidence: String) {
    ledger.push((name.to_string(), class.to_string(), evidence));
}

fn main() {
    let mut ledger: Vec<(String, String, String)> = Vec::new();
    let v = vec![10, 20, 30, 40];

    // --- the compiler still holds ---

    let generated = generated_fn(21);
    observe(
        &mut ledger,
        "macro-generated body",
        "still holds",
        format!("call result {}", generated),
    );

    let x = 1;
    let macro_value = uses_own_name!();
    observe(
        &mut ledger,
        "macro hygiene",
        "still holds",
        format!("outer x={} macro {}", x, macro_value),
    );

    let at_boundary = v.get(10);
    observe(
        &mut ledger,
        "slice bounds check",
        "still holds",
        format!("tenth item {:?}", at_boundary),
    );

    let pointer = v.as_ptr();
    let (raw_first, in_block) = unsafe {
        // Two operations in the same block: one is truly unchecked, the other is still checked.
        (*pointer, v.get(10))
    };
    observe(
        &mut ledger,
        "check inside an unsafe block",
        "still holds",
        format!("raw read {} but tenth item {:?}", raw_first, in_block),
    );

    // --- handed to the writer ---

    let raw = unsafe { *pointer.add(2) };
    observe(
        &mut ledger,
        "raw pointer read",
        "handed to the writer",
        format!("read {} safe equivalent {}", raw, v[2]),
    );

    let unchecked = unsafe { *v.get_unchecked(1) };
    observe(
        &mut ledger,
        "unchecked access",
        "handed to the writer",
        format!("read {} safe equivalent {}", unchecked, v[1]),
    );

    let contracted = unsafe { get_second_item(&v) };
    observe(
        &mut ledger,
        "handed-off contract function",
        "handed to the writer",
        format!("read {}", contracted),
    );

    // --- outside the language ---

    let absolute = unsafe { abs(-7) };
    observe(
        &mut ledger,
        "foreign function call",
        "outside the language",
        format!("result {}", absolute),
    );

    let text = CString::new("welcome").unwrap();
    let length = unsafe { strlen(text.as_ptr()) };
    observe(
        &mut ledger,
        "foreign length measurement",
        "outside the language",
        format!("length {} Rust equivalent {}", length, "welcome".len()),
    );

    // --- nobody checks it ---

    let doc_result = wrong_doc(3, 9);
    observe(
        &mut ledger,
        "doc comment accuracy",
        "nobody checks it",
        format!("doc says largest, result {}", doc_result),
    );

    observe(
        &mut ledger,
        "version string meaning",
        "nobody checks it",
        format!("written value {}", VERSION_STRING),
    );

    observe(
        &mut ledger,
        "body excluded from compilation",
        "nobody checks it",
        String::from("carries an undefined call and a type error, the program still compiled"),
    );

    // --- report ---

    println!("{:<34}{:<23}{}", "observation", "class", "evidence");
    for (name, class, evidence) in &ledger {
        println!("{:<34}{:<23}{}", name, class, evidence);
    }

    println!();
    for s in ["still holds", "handed to the writer", "outside the language", "nobody checks it"] {
        let count = ledger.iter().filter(|(_, k, _)| k == s).count();
        println!("{:<23}{}", s, count);
    }
    println!("total observations: {}", ledger.len());
}
```

```
observation                       class                  evidence
macro-generated body              still holds            call result 42
macro hygiene                     still holds            outer x=1 macro 99
slice bounds check                still holds            tenth item None
check inside an unsafe block      still holds            raw read 10 but tenth item None
raw pointer read                  handed to the writer   read 30 safe equivalent 30
unchecked access                  handed to the writer   read 20 safe equivalent 20
handed-off contract function      handed to the writer   read 20
foreign function call             outside the language   result 7
foreign length measurement        outside the language   length 7 Rust equivalent 7
doc comment accuracy              nobody checks it       doc says largest, result 3
version string meaning            nobody checks it       written value 0.1.0
body excluded from compilation    nobody checks it       carries an undefined call and a type error, the program still compiled

still holds            4
handed to the writer   3
outside the language   2
nobody checks it       3
total observations: 12
```

**MM2.** Four of the twelve observations fall into the still holds class, and two of those four
are the subject of this topic: the call to a generated body (`call result 42`) and a name inside
a macro not clashing with the caller's variable of the same name (`outer x=1 macro 99`). This
lesson runs the remaining ten once and sets them aside, deepening only these two.

#### Type Checking the Generated Body

The generating macro's call in the core brings a function named `generated_fn` into existence
without a single hand-written `fn` line. That this function returns `42` when called as
`generated_fn(21)` shows that a body stands in the source, but it does not show how deep that
goes. The example below sets up the same generation once more and puts the generated function
inside a constraint the macro knows nothing about — a generic bound.

```rust
// body_generalization.rs — a macro-generated body is put inside a generic bound.
macro_rules! generate_body {
    ($name:ident) => {
        fn $name(x: i32) -> i32 {
            x * 3
        }
    };
}

generate_body!(triple);

fn apply<F: Fn(i32) -> i32>(func: F, value: i32) -> i32 {
    func(value)
}

fn main() {
    let result = apply(triple, 7);
    println!("triple inside the generic bound: {}", result);
}
```

```
triple inside the generic bound: 21
```

**MM3.** This call to the generating macro brought only a function named `triple` into being;
the macro itself never tested whether the function satisfies the `F: Fn(i32) -> i32` bound
inside `apply` — that test was carried out by the compiler itself, exactly as it would be for
an ordinary function, when the generated source was passed into `apply`. The generated body is
checked not only when it is called directly but also when it is put inside a generic bound: the
macro has no advance knowledge of which context it will be used in, but the source it produces
is subject to the same rules in every context.

This distinction looks small, but its consequence is large: the place where the generating
macro is defined has no knowledge of which generic bounds `triple` will enter — that bound is
written much later, in an entirely separate place, when `apply` is defined. The two still work
together consistently, because the only link between them is the generated source's
**signature**: `fn triple(x: i32) -> i32`. The macro is responsible for producing this
signature, not for where it will be used; everything else, exactly as with a hand-written
function, is worked out jointly by the caller and the compiler.

#### Macro Hygiene

The hygiene macro's call in the core binds its own `x` inside itself and returns it; the
caller's own `x` (`1`) is unaffected by this call (`outer x=1 macro 99`). That result was shown
with a single call. The example below calls the same macro twice in the same scope, testing
whether it clashes with the outer name, with each other, or with neither.

```rust
// hygiene_second.rs — the same macro is called twice; its inner names clash neither with the outside nor with each other.
macro_rules! uses_own_name {
    () => {{
        let x = 99;
        x
    }};
}

fn main() {
    let x = 7;
    let first = uses_own_name!();
    let second = uses_own_name!() + 1;
    println!("outer x={} first={} second={}", x, first, second);
}
```

```
outer x=7 first=99 second=100
```

The outer `x` stays seven, both calls produce their own `99`, and the second call can use that
value in its own computation (`+ 1`) — the name inside the macro belongs to its own scope on
every expansion, clashing neither with the outer `x` nor with the previous expansion's `x`.
Hygiene here is not a rule that prevents a name clash; it is a mechanism in which every macro
expansion carries its own naming space: at the text level, the same word `x` appears three times
side by side, but all three are separate bindings as far as the compiler is concerned.

The practical payoff of this mechanism is this: whoever defines a macro can use as many helper
names as they want inside its body and never has to know which names the caller uses. Without
hygiene, it would be necessary to separately make sure that every helper name inside the macro
does not clash with names in the caller's scope — which would make the macro risky to use
anywhere the names cannot be predicted in advance. Rust's macro system closes this risk inside
the language itself, without writing a contract between the caller and whoever wrote the macro.

#### The Limiting Measurement

The previous two sections showed that the source a macro produces is checked just like real
Rust source. This checking carries a boundary: the macro operates only at the syntax level and
has no idea what the piece it substitutes actually means. The same macro is called with two
separate types to show this boundary.

```rust
// larger_picker.rs — the same macro is called with two types; both pass type checking.
macro_rules! generate_larger_picker_body {
    ($name:ident, $t:ty) => {
        fn $name(a: $t, b: $t) -> $t {
            if a > b { a } else { b }
        }
    };
}

generate_larger_picker_body!(larger_int, i32);
generate_larger_picker_body!(larger_bool, bool);

fn main() {
    println!("larger_int(3, 9)={}", larger_int(3, 9));
    println!("larger_bool(false, true)={}", larger_bool(false, true));
}
```

```
larger_int(3, 9)=9
larger_bool(false, true)=true
```

No type bound is written in the macro itself; `$t:ty` only says "a type name will come here",
without asking which types make this operation meaningful. Picking the larger of two `i32`
values is an ordinary comparison; the same syntax is just as valid for `bool` — `false` counts
as smaller than `true`, so the second function returns `true`. The compiler accepts both for the
same reason: both are types that support the comparison operator. Neither the macro nor the
compiler can say which call actually means something to the caller and which one compiles only
because the type happens to fit — only whoever reads the source can make that distinction.

This boundary does not invalidate the checking shown in the previous two sections; it
**narrows** it. Whether the generated body satisfies a generic bound and whether the name
inside it clashes with the caller's are both precise questions, and the compiler answers both
of them precisely. But "does this operation make sense for this type" is an entirely different
question, and the only tool the compiler has is type fitness: if a type supports the comparison
operator, it accepts that comparison without checking whether it matches the caller's intent.
For a tool that operates at the syntax level, this is not an unexpected limit but the definition
itself — a macro transforms one piece of text into another and has no say in what that text
means to whoever reads it.

#### Summary

- The four classes do not change throughout the course: still holds, handed to the writer,
  outside the language, nobody checks it; the distribution of the twelve observations is 4/3/2/3.
- This lesson deepened the two observations that fall into the still holds class: the body a
  macro generates is checked like ordinary source even inside a generic bound, and the name
  inside a macro carries its own scope on every expansion, never clashing with the caller's.
- Checking the generated body is not limited to a direct call: a generated function is tested
  by the same rules when it is put inside a trait bound as well.
- A macro operates at the syntax level: when the same macro is called with two types that are
  semantically far apart, both compile, because the compiler only checks whether the types fit.

#### Next Step

This lesson showed that generation happens within the same compilation step — the same file:
the macro definition and the macro's call could stand together, and the compiler could do the
defining, the expanding, and the type checking all in a single pass. The next lesson measures
the same still holds class from an entirely different setup: generation no longer happens in
the same file but in a separate compilation step, and the consuming source can only see the
generated source once the compiler is invoked a second time.

### Procedural Macros

Source: https://academia.sh/en/courses/rust-advanced/procedural-macros

A program calls the compiler twice with std::process::Command — first a procedural macro crate, then the source that uses it — and prints only the compiled/not-compiled classification and the output match; the generated body again falls into the still holds class, but this time generation happens not in the same file but in a separate compilation step.

The previous lesson showed that a macro definition and the macro's call can stand in the same
file: the compiler read the definition, expanded it, and checked the source it produced, all
in a single pass. This lesson measures the same "still holds" class from an entirely different
setup. A procedural macro is an ordinary Rust function that processes source text by hand and
returns new source text — but this function has to be compiled on its own, in a separate crate,
and the source that uses it can only work once the compiler finds that crate as an
already-compiled binary. Generation no longer happens in the same pass; it happens across two
separate compilation steps.

This two-step nature shows up in the language's own vocabulary too. Declarative macros worked
with the language's own pattern-matching mechanism — the way a `match` arm meets a value, the
incoming tokens were matched against a pattern and a fixed template was put in its place. A
procedural macro is not tied to such a pattern-matching mechanism: it can process the token
stream it receives as input like any Rust code it likes — it can build a loop, write a
condition, accumulate into a helper data structure — and in the end produce and return a new
token stream. This is a different kind of power from a declarative macro's syntactic
templating: generation is now **programmable**. The cost of this power starts right here —
programmable generation requires an independent program the compiler can run, and an
independent program cannot exist in the same pass as the rest of the source.

This difference can be summed up in a single question: a declarative macro only **matches a
pattern and substitutes**, a procedural macro **runs**. This gap lines up exactly with this
course's measurement axis, which looks not at which tool produced the core but at when and
where generation actually happens.

#### Why Generation Moves to a Separate Step

A procedural macro function takes a token stream (`TokenStream`) as input and returns another
token stream as output. This transformation does not run at runtime; it runs at **compile
time** — but for it to run, the compiler itself has to be able to **call** this function like a
subroutine while it is still processing the source it is compiling. For a function to be
callable in the middle of the compilation process, that function has to already exist as a
linkable binary before compilation starts — it cannot be compiled in the same pass, at the same
time, as the rest of the source. This is why the language itself imposes a restriction: a crate
containing a procedural macro must be compiled with a separate crate type named `proc-macro`,
and every crate that uses it must take that crate as a dependency in its own build, already
compiled.

This is a fundamental split from the declarative macro in the previous lesson. There, the
macro definition and its call could stand in the same file, because expansion was a textual
substitution and the compiler could do it in a single pass. Here, generation is not a text
substitution but the output of a program that runs; that program first has to be brought into
existence on its own, through a complete compilation step.

Rust has three separate forms of procedural macro: the form that takes a call-style invocation
as input, the attribute macro that takes the item an attribute line affects, and the derive
macro that expands the derive attribute itself. All three carry the same restriction — all
three must be compiled in their own crate — and all three share the same kind of function
signature: a token stream in, a token stream out. An attribute macro is triggered by a line
written above a function or struct declaration and takes the item beneath it as a token stream;
a derive macro, meanwhile, expands the `#[derive(...)]` line itself, as seen in the Derived
Traits lesson, and can only produce a new implementation — it cannot change the item it is
written on. This lesson measures the plainest form, the call-style one, because this form can
produce a fixed output without even parsing its input, and that keeps the measurement's focus
from drifting away from the generation mechanics.

#### Measurement: Two-Stage Compilation

The measurement is set up from inside the program. A Rust program calls the compiler twice
with `std::process::Command`: first it compiles the procedural macro crate with
`--crate-type proc-macro`, then it compiles the source that uses it, linking it to that crate
with `--extern`, and finally it runs the resulting binary and checks its output against what is
expected. The pattern matches the one the Rust's Design Goals lesson adopted from the Go
Fundamentals course: the compiler is called as a subprocess, and only its exit status and
output are read; error text, path, duration, and machine name are never read.

The measurement itself provides two things at once. First, the measurement is deterministic:
both sources sit inside the program as fixed string constants, neither is read from outside, so
the same program compiles the same two sources on every run and prints the same three lines.
Second, the measurement erases its own trace: the working directory is opened inside a
temporary folder, and the program's last line removes that folder entirely — it leaves no
persistent file behind and does not let one run's leftovers affect the next.

**MM4.** The two crates' source text is kept inside the program as constant strings. The crate
source begins with the line `extern crate proc_macro;` — this brings in the one non-third-party
crate that sits ready in the compiler's sysroot. The generated token stream is built by parsing
a string constant (`.parse().unwrap()`) rather than by hand-building a token tree; this is the
most direct route that requires no dependency outside the crate.

Real procedural macros mostly process the input token stream too — they look at which name,
which fields appear, and shape the output accordingly. Parsing a token stream by hand, by
building a token tree, is possible without a third-party crate as well, but this lesson never
reads the input at all: the `_input` parameter is left unused, because what is being measured
is not token-handling skill but **which compilation step** generation happens in. A macro that
reads its input and branches on it would be subject to the same two-stage compilation; reading
the input does not change where generation happens, only the content of the generated text.

```rust
// generation.rs — a procedural macro crate is compiled from inside the program, then
// the source that uses it is compiled and run with a second call.
use std::env;
use std::fs;
use std::path::PathBuf;
use std::process::Command;

const CRATE_SOURCE: &str = r#"
extern crate proc_macro;
use proc_macro::TokenStream;

#[proc_macro]
pub fn generate_body(_input: TokenStream) -> TokenStream {
    "fn generated_value() -> i32 { 42 }".parse().unwrap()
}
"#;

const CONSUMER_SOURCE: &str = r#"
macro_crate::generate_body!();

fn apply<F: Fn() -> i32>(func: F) -> i32 {
    func()
}

fn main() {
    let direct: i32 = generated_value();
    let inside_generic_bound: i32 = apply(generated_value);
    println!("generated_value={} inside_generic_bound={}", direct, inside_generic_bound);
}
"#;

const EXPECTED_OUTPUT: &str = "generated_value=42 inside_generic_bound=42\n";

fn succeeds(command: &mut Command) -> bool {
    command.output().map(|c| c.status.success()).unwrap_or(false)
}

fn classify(successful: bool) -> &'static str {
    if successful { "compiled" } else { "not compiled" }
}

fn main() {
    let workdir = env::temp_dir().join(format!("procedural-{}", std::process::id()));
    fs::create_dir_all(&workdir).unwrap();

    let crate_path = workdir.join("crate.rs");
    fs::write(&crate_path, CRATE_SOURCE).unwrap();

    let crate_binary: PathBuf = workdir.join(format!("libmacro_crate.{}", env::consts::DLL_EXTENSION));
    let crate_compiled = succeeds(
        Command::new("rustc")
            .args(["--edition", "2021", "--crate-type", "proc-macro", "--crate-name", "macro_crate"])
            .arg("-o").arg(&crate_binary)
            .arg(&crate_path),
    );
    println!("crate: {}", classify(crate_compiled));

    let consumer_path = workdir.join("consumer.rs");
    fs::write(&consumer_path, CONSUMER_SOURCE).unwrap();
    let consumer_binary = workdir.join("consumer");

    let consumer_compiled = crate_compiled
        && succeeds(
            Command::new("rustc")
                .args(["--edition", "2021"])
                .arg("--extern").arg(format!("macro_crate={}", crate_binary.display()))
                .arg("-o").arg(&consumer_binary)
                .arg(&consumer_path),
        );
    println!("consumer: {}", classify(consumer_compiled));

    let matched = consumer_compiled
        && Command::new(&consumer_binary)
            .output()
            .map(|c| c.stdout == EXPECTED_OUTPUT.as_bytes())
            .unwrap_or(false);
    println!("output matched: {}", matched);

    let _ = fs::remove_dir_all(&workdir);
}
```

```
crate: compiled
consumer: compiled
output matched: true
```

**MM5.** The binary file's extension is not hard-coded anywhere; `env::consts::DLL_EXTENSION`
gives the platform's own shared-library extension, and the program reads it and appends it to
the file name. This is a way to build a portable example without printing a real machine name
or directory path: the extension is never printed on screen, it is only used, invisibly, inside
the file name. The `--extern macro_crate=<path>` binding introduces the binary the first
compilation produced to the second compilation under a crate name — the consumer source writes
its call to the generating macro exactly as it would write a call into any standard-library
crate, prefixing it with the crate name.

This binding step requires no package registry and no dependency resolver; `--extern` only
maps a name to a file path, and the compiler reads the binary at that path directly. The
measurement is therefore set up without opening a network connection or an outside registry,
using only two `rustc` calls and a file path passed between them — the course's rule against
using a third-party crate is not broken here either: the only crate linked is `macro_crate`,
which the program produces itself.

The result is three lines: the crate compiled, the consumer source compiled, the output matched
what was expected. All three are classifying results — none of them carries error text,
duration, or a path. The `consumer` source uses the generated function in two separate ways:
once through a direct call, and once from inside the `F: Fn() -> i32` bound of the `apply`
function. Both give the same result (`42`) and both compile — the generated body, exactly as
seen in the previous lesson, is checked here too as if it were an ordinary function inside a
generic bound. The only difference is **where** generation happens: in the previous lesson,
generation was inside the same pass; here, generation is already finished and enters the second
compilation as a compiled binary.

**MM6.** The second line of the `consumer` source, which tests the generic bound, is not an
accidental flourish: this lesson deliberately repeats the same test to show that the "still
holds" claim from the previous lesson is not specific to that single setup and remains true
once generation moves to a separate compilation step. The two lessons use different generation
mechanisms — one a textual substitution, the other the running of a separate program — but the
source each one produces has the same status in the compiler's eyes: ordinary Rust source.

This similarity has a limit. The `compiles` function built in the Go's Design Goals lesson
tested a single source with a single compiler call; here, two sources and two sequential calls
are needed, because the second call depends on the first one's output. The `consumer_compiled`
variable starting with `crate_compiled &&` makes this dependency visible right in the code: if
the crate did not compile, there is no point trying to compile the consumer source, because
there is no binary to link against. The same logic is repeated once more in the `matched`
variable — if the consumer source did not compile, there is no binary to run either. This
chained short-circuit also documents the order of generation in the code itself: once one step
fails, the following steps are never even attempted, and the `false` value on the last line just
says "the chain did not complete" without distinguishing which step it stopped at.

The Derived Traits lesson showed that an attribute line can give rise to an implementation with
no body at all in the source, and that how many times this body ran could not be counted. This
lesson does not repeat that counting claim: what is measured here is not how many times
something ran, but which compilation step generation happens in, and whether that step actually
passed. The two lessons ask different questions but share the same observation: a body that is
not hand-written in the source is, from the compiler's point of view, no different from an
ordinary body.

#### The Limiting Measurement

A procedural macro has to live in its own crate, and this is a restriction of the language, not
a preference. A piece of code that tries to compile the crate source (`CRATE_SOURCE`) and the
consumer source (`CONSUMER_SOURCE`) in a single file with a single compilation call is rejected
by the compiler: the `proc-macro` crate type can only export macro definitions, and a crate
cannot call the procedural macro it defines itself from within itself. This rejection is
described in prose rather than placed in a code block, because under the course's general rule,
source that does not compile never goes into a code block.

This restriction directly shaped the measurement's design. In the previous lesson, a single
`rustc` call was enough, because the macro definition and its call were part of the same pass.
Here, two separate `rustc` calls are needed, because the crate first has to **exist** — the
second call takes the binary the first one produced as an input. The procedural macro falling
into the "still holds" class therefore also exposes an architectural decision of the language:
the code the compiler runs during expansion has to exist **before**, and **separately from**,
the source being compiled itself. Checking the generated body does not change, but when and in
how many stages that checking happens does.

The second face of the restriction is in **what the crate is allowed to export**. A crate
compiled with the `proc-macro` crate type can only export procedural macro definitions; an
ordinary `pub fn` or a `pub struct` cannot be exported from the same crate. This is an
extension of the rejection in the previous paragraph: the crate type determines the crate's
**role**, and that role is limited to "macro generation" alone. If a project wants to offer both
an ordinary function and a procedural macro, it has to split the two into two separate crates —
one of the `proc-macro` type, the other an ordinary library crate. This split is followed in
the measurement's setup as well: `crate.rs` exports nothing but the generating macro.

This restriction carries one more cost: a two-crate structure always requires more compilation
steps than a single-crate one. In the previous lesson, when a change was made, the compiler read
a single file and produced a single binary; here, even the smallest change to the crate source
requires the crate to be recompiled first, then the consumer source to be recompiled against
that new binary. This is the price of generation being programmable: every generation form that
goes beyond a textual substitution brings its own separate compilation timeline along with it.

#### Summary

- A procedural macro is an ordinary Rust function that processes an input token stream and
  returns an output token stream; this function must be compiled in its own `proc-macro` crate,
  and only macro definitions can be exported from that crate.
- The measurement was set up from inside the program: the compiler was called twice, in
  sequence, and only the compiled/not-compiled classification and the output match were printed
  — error text, path, duration, and machine name were never read.
- The generated body falls into the still holds class in this setup too: it is checked like
  ordinary source both when it is called directly and when it is used inside a generic bound;
  the difference is that generation happens not in the same pass but in a separate compilation
  step.
- A procedural macro having to live in its own crate is not a preference but a restriction of
  the language: this is why the measurement required two separate, sequential compilation calls
  instead of one.
- This restriction carries a cost: even the smallest change to the crate source requires both
  the crate and the source that uses it to be recompiled.

#### Next Step

These two lessons showed how generated source is checked in two different setups: once through
a textual substitution in the same pass, once through a program run in a separate compilation
step. Neither has yet asked a question the two share — why generation is preferred over writing
by hand. The next lesson, which is also this topic's final lesson, takes up that question: the
same structure is built separately with a macro-written version and a hand-written version,
shown to produce the same result, and the macro version is measured for how many lines it
shortens the same job by. It is then shown, with an example, that this shortening carries no
guarantee of correctness, only a gain in readability.

### Domain-Specific Languages

Source: https://academia.sh/en/courses/rust-advanced/domain-specific-languages

The same routing structure is built separately with a macro-written version and a hand-written version, shown by running it to produce the same result, and the macro version is measured for how many lines it shortens the source by; but a macro's output being readable does not bring correctness — a logically wrong path-function binding passes type checking just as easily.

The previous two lessons showed how a generated body is checked in two different setups: once
through a textual substitution in the same pass, once through a program run in a separate
compilation step. Neither has yet asked a question they share: why is generation preferred over
writing by hand? This topic's last lesson takes up that question. A macro can build an
interface that looks, from the caller's point of view, like a small language of its own — with
its own syntax, its own shortcuts; this is called a domain-specific language. This lesson
builds the same structure with two versions, verifies by running it that both produce the same
result, counts how many lines the macro version shortens, and finally shows that this
shortening carries no guarantee of correctness.

The term domain-specific language points here not to a broad framework but to a narrow,
concrete use: whoever reads the source can write directly in the words of the problem domain (a
path, a handler), without ever seeing the `match` block, function definition, or other
mechanics underneath. This interface requires no new rule from the compiler — the source that
appears after the macro expands is, as seen in the previous two lessons, indistinguishable from
ordinary Rust source. What is gained is only the way the source is **written**; the way it is
**checked** does not change at all.

This definition sets the term domain-specific language apart from its broad use in
programming-language theory, where the term mostly points to an independent language with its
own parser and its own runtime. Here, there is no second language — there is a single language,
Rust itself, and the macro only fits that language's syntax into a shorter pattern for a
specific problem. This is why the interface built in this lesson carries no parser of its own;
parsing is already done by the compiler's own parser, as part of macro expansion.

#### The Same Structure, Two Versions

A routing table — a table that binds a path string to a function — can be built either with a
macro version or by hand. The macro version lists path-handler pairs like a list; the
hand-written version pours the same table into a `match` block's arms. Both do the same job:
they turn a path string into a result.

The hand-written form requires repeating three things for every new path: the `match` block
itself, an arm's syntax, and the presence of a catch-all arm. This repetition is not a flaw —
as seen in the Pattern Matching lesson, `match` demanding exhaustiveness is what keeps the
source safe. But the repeated part carries no new information beyond the path-handler pair
itself: the `match`, `path {`, and `_ => -1` lines are identical in every routing table. The
macro writes this fixed pattern once, inside its own definition, and reproduces it on every
call; the caller writes only the part that changes — the pairs themselves.

```rust
// router.rs — the same routing table is built with a macro version and its hand-written equivalent.
macro_rules! build_router {
    ($name:ident { $($path:literal => $handler:expr),+ $(,)? }) => {
        fn $name(path: &str) -> i32 {
            match path {
                $($path => $handler,)+
                _ => -1,
            }
        }
    };
}

fn home_page() -> i32 { 200 }
fn about_page() -> i32 { 200 }

build_router!(route_with_macro {
    "/home" => home_page(),
    "/about" => about_page(),
});

fn route_by_hand(path: &str) -> i32 {
    match path {
        "/home" => home_page(),
        "/about" => about_page(),
        _ => -1,
    }
}

fn main() {
    for path in ["/home", "/about", "/unknown"] {
        let m = route_with_macro(path);
        let e = route_by_hand(path);
        println!("{:<12}macro={} hand={} same={}", path, m, e, m == e);
    }
}
```

```
/home       macro=200 hand=200 same=true
/about      macro=200 hand=200 same=true
/unknown    macro=-1 hand=-1 same=true
```

**MM7.** The macro definition's pattern is made of three parts: `$name:ident` captures the name
of the function to be generated, `$path:literal => $handler:expr` repeats the pair one or more
times (`+`), and `$(,)?` accepts an optional trailing comma after the last pair. Together, these
three parts turn the language's own syntax into a list-like notation: the caller does not write
`match`, only lines up path-handler pairs inside curly braces. All three paths (`/home`,
`/about`, `/unknown`) give exactly the same result in both versions; the `same` column verifies
this one by one.

This expressive interface was built with a declarative macro, not a procedural one — this
choice is not arbitrary. As the previous lesson showed, a procedural macro has to be compiled in
a separate crate, and using it requires a two-stage compilation. The routing table built here is
nothing more than a repeated syntax pattern; wrapping this kind of repetition needs no
hand-processing of a token stream, no separate crate, and no two-stage compilation — a
declarative macro's pattern-matching mechanism is already enough. A procedural macro is
preferred when real computation is needed on the input, not just to repeat a pattern.

The generated `route_with_macro` function and the hand-written `route_by_hand` function have the
same status in the compiler's eyes: both are an `fn`, both carry the same signature, both run
the same `match` logic. This confirms, once again, the finding of the previous two lessons — the
generated body falls into the "still holds" class — but in this lesson the real question is not
whether the body is checked, it is how much shorter **writing** it becomes.

This equivalence has a consequence: when choosing between the two versions, runtime behavior can
never be a criterion — both produce exactly the same thing. The only axis that decides the
choice is how the source is **read** and how it is **written**. As the number of paths grows,
this gap becomes more pronounced: if thirty paths had to be added instead of three, the
hand-written version would repeat the `match` arm's syntax thirty times, while the macro version
would only add thirty pairs. This lesson does not leave that gap as intuition; it measures it
with a number.

#### The Source's Own Length

There is no direct way to measure at runtime how many lines a macro actually generates —
expansion happens during compilation, and only the result ever reaches the running program's
hands. What can be measured is the two versions' length **in the source**: how many lines the
macro call itself is, and how many lines the hand-written `match` block doing the same job is.
These two source texts can be kept as string constants and their line counts compared.

This is a separate measurement, independent of the run in the previous section. The previous
section showed that the two versions produce **the same result**; this section shows how much
source each one does **the same job** with. The two complement each other: without the first,
the second would be meaningless — there is no point praising a short but wrong version for being
short. This is why this lesson verified the equivalence by running it before moving to the line
count; the order was chosen deliberately, since the reverse would have created the impression
that brevity stands in for correctness.

```rust
// line_count.rs — the same routing definition is kept in two versions, and the line counts are compared.
fn main() {
    let macro_version = "build_router!(route_with_macro {\n    \"/home\" => home_page(),\n    \"/about\" => about_page(),\n});";
    let hand_version = "fn route_by_hand(path: &str) -> i32 {\n    match path {\n        \"/home\" => home_page(),\n        \"/about\" => about_page(),\n        _ => -1,\n    }\n}";

    let macro_lines = macro_version.lines().count();
    let hand_lines = hand_version.lines().count();
    println!("macro version: {} lines", macro_lines);
    println!("hand version: {} lines", hand_lines);
    println!("lines saved: {}", hand_lines - macro_lines);
}
```

```
macro version: 4 lines
hand version: 7 lines
lines saved: 3
```

**MM8.** This measurement was set up for the same reason that explained why the `--extern` path
was never printed on screen in the previous lesson: there is no way to read a macro's expansion
at runtime, so the measurement measures the one thing that can be carried into runtime — the
source's own text. The Rust's Design Goals lesson measured the rejection of an implicit type
conversion by the same method, comparing the character counts of two source lines; here the unit
is lines, not characters, but the method is the same: treating the source itself as data,
instead of running it.

The hand-written routing is seven lines: the function signature, the `match` header, two arms,
the catch-all arm, two closing braces. The macro call is four lines: the call itself, two
path-handler pairs, the closing. The three-line gap between them comes from the fact that `fn`,
`match`, and the closing braces never have to be written on the macro's side at all — these
three lines are written **once**, inside the macro's own definition, and reproduced on every
call. This ratio does not change as the number of paths grows: every new path-handler pair adds
one line to both versions, but the constant three-line gap between the two versions always stays
the same.

This fixed cost also explains why the measurement prints an absolute number, "lines saved",
rather than a ratio, "how many times shorter". For two paths the gap is three lines, and for
thirty paths the gap would still be three lines — the ratio shrinks, the absolute gap does not
change. This is why the measurement prints the difference, not the ratio: under the course's
rule about numbers, a claim must carry only the quantity that was actually measured, and what
can be measured here deterministically is the line gap between two fixed sources, not a
generalization.

#### The Limiting Measurement

A readable version does not bring correctness. The macro never asks whether the path-handler
pairs the caller writes are **meaningful** — it only checks whether each pair is a string
constant and an expression that returns `i32`. This boundary adds to the measurements in the
previous two sections; it does not invalidate them: it remains true that the macro version
produces the same result and shortens the source; but these two gains carry no answer at all to
a third question — whether the table that was built is correct. The example below builds, with
the same macro, a table where a path is mistakenly bound to a function other than the one it
should be.

```rust
// wrong_but_right_type.rs — a logically wrong mapping passes type checking too.
macro_rules! build_router {
    ($name:ident { $($path:literal => $handler:expr),+ $(,)? }) => {
        fn $name(path: &str) -> i32 {
            match path {
                $($path => $handler,)+
                _ => -1,
            }
        }
    };
}

fn successful() -> i32 { 200 }

build_router!(faulty_router {
    "/home" => successful(),
    "/error" => successful(),
});

fn main() {
    println!("/error -> {}", faulty_router("/error"));
}
```

```
/error -> 200
```

**MM9.** The `/error` path, as its name suggests, is expected to represent a failure condition;
but the line that builds the table is mistakenly bound to the `successful()` function, and the
result is `200`. The macro does not reject this, because it has no information with which to
reject it: the `$handler:expr` pattern only says "an expression returning `i32` will go here",
and never asks whether that expression is consistent with the meaning of the path it is called
for. The compiler stays silent for the same reason — `successful()` really does return `i32`,
the signature holds, and type checking passes. The generated body falls into the "still holds"
class once again, because type checking really is running; but here type checking never answers
the question "is this correct", only the question "is this the right type".

In this example, the same mistake could equally well have been written by hand — binding a wrong
function to an arm inside `route_by_hand` would have compiled the same way. The macro does not
**make the mistake easier** here; it just does not **hide** it either. The difference is that
both versions carry the same weakness, but the macro version makes that weakness reachable with
fewer lines and less writing: adding a wrong pair is an act that demands less attention than
changing an arm inside a thirty-line `match` block. A shorter source makes reviewing the source
easier, but it does not replace review.

This observation does not add a fifth class to the course's four — "wrong but type-compatible"
is not a separate class, it is a detail inside the "still holds" class itself. The compiler
really is checking here too, skipping no step; it is only that what it checks is narrower than
the question a human reading the source would ask: "is this expression of the expected type",
not "is this binding correct".

This is a boundary that stands in the same class as the observations of the previous two lessons
but is shown from a different angle. The Declarative Macros lesson showed that the same macro
can be called with two semantically distant types, `i32` and `bool`, that both compile, and that
the macro cannot say which one is meaningful. Here the type is fixed (`i32`); what changes is
**which expression** a string is bound to — but the result is the same: the macro operates at
the syntax level, unable to see that two different bindings are separated **in meaning**, seeing
only that both are formally correct. A readable version only lets the writer express their
intent more briefly; whether that intent was built correctly still has to be checked by whoever
reads it.

The distinction seen in the Procedural Macros lesson still holds here too: even if a procedural
macro read its input token and performed a more complex operation on it, it would still look
only at **type fitness** — because in Rust, type checking always runs after expansion, on the
generated source, no matter which macro form is used. Even though the two macro forms
(declarative, procedural) have different generation mechanisms, the source each one arrives at
goes through the same checking process, and that process's boundary is the same in both: it
answers only the first of the two questions, is the type correct, is the meaning correct.

#### Summary

- The same routing structure was built with a macro version and a hand-written version; for
  three paths, both produced exactly the same result, and this equivalence was verified by
  running it and comparing each one individually.
- The generated body falls into the still holds class in this lesson too: the function the
  macro call produces is checked by the same rules as the hand-written function.
- The macro version shortened the same job by three lines; this shortening comes from the macro
  writing its fixed header-and-closing cost once and reusing it on every call, and the absolute
  gap itself does not change as the number of paths grows.
- A readable macro version brings no guarantee of correctness: a logically wrong path-function
  binding compiles just as well, because its type checks out.
- The only thing the macro can check is form — whether an expression is of the expected type;
  there is no mechanism that checks whether that expression matches the caller's intent.
- A declarative macro was chosen to build an expressive interface; a procedural macro is
  preferred only when real computation on the input is needed, not to wrap a fixed pattern.

#### Next Step

This topic's three lessons showed that generated source, across three separate setups — in the
same file, in a separate compilation step, inside an expressive interface — always fell into the
same class, still holds. The boundary that stayed constant across all three was the same: the
compiler checks the generated source's **type**, not its **meaning**. The next topic, Low-Level
and Interoperability, deepens this boundary: after these three lessons in which the compiler's
checking never stopped, the next lesson enters a block where checking **really does** stop — and
even there, checking does not shut down entirely; only a few operations open up into the "handed
to the writer" class, and everything else, exactly as here, keeps on holding.

## Low-Level and Interoperability

Where the compiler hands its checking off to the writer, where a foreign function's signature is trusted without ever seeing its body, the constraints a target running in a browser brings, and what a no_std environment takes away.

### `unsafe` Rust

Source: https://academia.sh/en/courses/rust-advanced/unsafe-rust

That an unsafe block does not close off the compiler's entire checking, only the checking that covers a few specific operations, is shown by running code: within the same block, a raw pointer read stays unchecked while a slice query is still checked.

The previous topic showed that no matter how different the body a macro produces looks,
it still passes through type checking like ordinary source: the compiler checked a
generated function exactly as strictly as a hand-written one. This topic asks the same
question from the opposite direction: is there a form where the compiler does not perform
this checking at all, where it stops checking and leaves the matter to the writer? The
answer is the `unsafe` keyword — but this lesson's finding is that the keyword does not
open as wide a door as it appears to.

The Borrow Checker lesson, from the Ownership and the Memory Model course, established how
the compiler tests ownership and borrowing rules, which forms it rejects. This lesson does
not show that checker closing — it shows the opposite: that it does not close, only that
the obstacle in front of a few operations is lifted. The Standard Library and Streams
course asked who gives the guarantee for a behavior: the interface, the implementation, or
the caller — the parties to a contract. What this lesson asks is different: where the
compiler's own checking stops, without looking at either party to that contract.

This gap exists for a reason: the rules the borrow checker tests do not cover every
correct program that could be built. A structure such as a circular buffer, a node pool,
or a driver that accesses a hardware register directly can carry an ownership pattern more
complex than the borrow checker can follow — the checker may reject that pattern even when
it is correct, because its testing rule comes not from the pattern itself but from the
class of patterns the checker can follow. `unsafe` is the escape valve that closes this
gap: the writer can say "this is correct" for a reason the checker cannot test. The
standard library's own growable array and slice types are built the same way — while they
present a safe interface to code that looks from the outside, their own bodies use the
same raw pointer operations. This lesson does not look inside those bodies; it only
measures how the same permission works in an ordinary program.

#### The `unsafe` Block's Limited Permission

Constructing a raw pointer does not require `unsafe`. The form `&value as *const i32` is
an ordinary expression, it compiles, and it skips no checking — what is constructed is
only an address, not yet read. Borrow rules are not in play on this line either, because
raw pointers are not among the types the borrow checker tracks; no bookkeeping such as
ownership, lifetime, or uniqueness is kept for them.

The restriction begins when there is an attempt to **read** the pointer. The compiler
cannot know that a genuinely valid value sits where a raw pointer points — the pointer
might be null, the memory it belongs to might have been returned, or it might just carry
a made-up address. Because the compiler cannot distinguish among these three
possibilities, it requires the read to sit inside an `unsafe` block: the code compiles,
but the declaration "there is a contract here and I am meeting it" belongs to the writer.
The same restriction applies to calling a function whose body is marked `unsafe` — the
function's name says the caller must satisfy a precondition, but the compiler does not
test that condition.

This permission list is short and fixed by the language itself: the compiler reads which
operations require `unsafe` from the source, not from the caller's intent. The run below
places three operations that do not require `unsafe` side by side: constructing a raw
pointer, shifting it in a way defined against overflow, and comparing two pointers with
each other. Because none of the three reads the value, they break nothing even if they
operate on an invalid address — what would break something is attempting to access the
data at that address.

```rust
// allowed.rs — which pointer operations do not require unsafe
fn main() {
    let v = vec![10, 20, 30, 40];
    let pointer = v.as_ptr();                       // construct
    let shifted = pointer.wrapping_add(1);           // shift, defined against overflow
    let equals_self = pointer == pointer;
    let equals_shifted = pointer == shifted;

    println!("equals self: {}", equals_self);
    println!("equals shifted: {}", equals_shifted);
}
```

```
equals self: true
equals shifted: false
```

What these three operations share is that their results, on their own, expose no memory:
knowing whether an address equals itself differs from knowing the value at that address.
**LL1.** The `wrapping_add` call here was chosen deliberately — the language's pointer
method carrying this name returns a defined value even when the shift overflows, and for
that reason does not demand `unsafe`; by contrast, the `add` method that performs the same
shift asks the caller, as a precondition, that the result stay inside the same allocated
memory block or one byte past it, and because the compiler does not test this
precondition, calling it requires `unsafe`. The two methods do the same arithmetic; what
differs between them is what is guaranteed in the case of overflow.

By contrast, reading or writing a raw pointer and calling a function whose body is marked
`unsafe` both require an `unsafe` block — because both operations rest on an assumption
the compiler cannot test (that the address is valid, that the function's precondition is
met). The next two sections show this distinction by running code within the same block.

#### Checking That Continues and Checking That Stops, in the Same Block

An `unsafe` block does not affect every line written inside it to the same degree. The run
below places two different operations inside the same block: one a direct read from a raw
pointer, the other a bounds-checked query on a slice. Both stand under the same `unsafe`
keyword, but only one is exempt from checking.

**LL2.** A four-element integer slice is used, and the queried index (the tenth item)
lies outside this slice's bounds.

```rust
// block_a.rs — checking that continues and checking that stops, in the same unsafe block
fn main() {
    let v = vec![10, 20, 30, 40];
    let pointer = v.as_ptr();

    // Two operations in the same block: one reads through a raw pointer (unchecked),
    // the other makes a safe slice query (v.get) (still checked).
    let (first_raw, tenth) = unsafe { (*pointer, v.get(10)) };

    println!("raw read: {}", first_raw);
    println!("tenth item: {:?}", tenth);
}
```

```
raw read: 10
tenth item: None
```

Both lines ran inside the same `unsafe` block, but they took different paths. `*pointer`
read the raw pointer directly — there is no bounds check here at all, whatever the address
points to is what gets read. `v.get(10)`, by contrast, passed through the slice's own
method; this method's body is not marked `unsafe`, and the bounds check inside it kept
running, which is why, asked for something past the fourth element, it returned `None`
instead of crashing. **LL3.** Here the `unsafe` block only lifted the obstacle in front of
the `*pointer` line; the `v.get` call was already a safe method, and its entering an
`unsafe` block did not change the checking inside its own body.

This observation is the lesson's central finding: `unsafe` is not a key, it does not
unlock all of the compiler's locks at once. The block grants permission only for the
specific operations written inside it — everything outside those operations, even inside
the block, stays subject to its own rules. The borrow checker is still in play here too:
`v`'s ownership, `pointer`'s having been derived from it, and both their scope rules — none
of this was suspended because of the `unsafe` block. The only thing suspended was the
bounds checking on the raw pointer read itself.

The run above deliberately showed this by placing the safe call inside the `unsafe` block;
in ordinary code there is no such requirement, `v.get(10)` does not require an `unsafe`
block on its own. Common practice is to keep an `unsafe` block limited to only the lines
that genuinely need it — the wider a block grows, the harder it becomes to tell afterward
which line rests on which contract. Writing two operations in the same block in this
lesson is meant to make concrete why that practice exists: even when a block is kept wide,
where checking continues and where it stops can be read from the source, but that
readability is not automatic — it requires tracing line by line.

#### Three Delegated Operations

Like the `*pointer` read in the previous section, the lines where checking is delegated do
not stand alone — each of them can be placed side by side with a safe counterpart that
does the same job. The run below compares three separate delegated operations with their
safe counterparts: a read through raw pointer arithmetic, the slice's unchecked access
method, and a call to an `unsafe` function that carries a contract.

Though these three forms do the same job, where each is used differs. Raw pointer
arithmetic is used when a memory block needs to be walked independently of Rust's own
slice type — for instance, when reading a memory region returned from a foreign function, a
topic taken up in the next lesson. `get_unchecked` is used on a slice when the validity of
the index has already been proven somewhere else (by a loop bound, by a search result). A
contracted `unsafe` function is used to package these two forms together and present an
interface where it is enough for the caller to read only the signature — the contract is
still there, but its body can move to a different file.

**LL4.** The slice being compared is again `[10, 20, 30, 40]`; the raw pointer starts at
the second index and is shifted two positions further, the unchecked access goes straight
to the second index.

```rust
// block_b.rs — three delegated operations and their safe counterparts
unsafe fn get_second_item(v: &[i32]) -> i32 {
    // Contract: the caller guarantees that v carries at least two items.
    *v.get_unchecked(1)
}

fn main() {
    let v = vec![10, 20, 30, 40];
    let pointer = v.as_ptr();

    let raw = unsafe { *pointer.add(2) };
    let unchecked = unsafe { *v.get_unchecked(1) };
    let contracted = unsafe { get_second_item(&v) };

    println!("raw pointer read: {} (safe counterpart {})", raw, v[2]);
    println!("unchecked access: {} (safe counterpart {})", unchecked, v[1]);
    println!("contract-delegated function: {} (safe counterpart {})", contracted, v[1]);
}
```

```
raw pointer read: 30 (safe counterpart 30)
unchecked access: 20 (safe counterpart 20)
contract-delegated function: 20 (safe counterpart 20)
```

All three lines' results are identical to the bounds-checked access done with `[]`. This
is not a coincidence: the pointer advanced with `add(2)`, the `get_unchecked(1)` call, and
the call inside `get_second_item` all read the slice's memory layout correctly — only who
guarantees that correctness changes. In safe `[]` access, the guarantee is given by the
slice's own method, and it stops with a panic at run time if the index is out of bounds.
In `get_unchecked` and raw pointer arithmetic, nobody gives that guarantee; choosing the
correct index is the caller's obligation.

The `get_second_item` function exists to show a form that makes this obligation visible in
the source: the `unsafe` word in front of its signature says the body expects a
precondition to be met before it is called. This is a form the language itself recognizes
— the function's name and signature do not hide the unchecked access inside its body from
the caller's eye, they mark it instead. Marking a function `unsafe` also does not
automatically make every line inside its body unchecked: within the body too, only the
lines that genuinely require `unsafe` (here, the `get_unchecked` call) carry that status,
the rest is ordinary checked code. **LL5.** The party that calls the function as `unsafe`
has to verify for itself that `v` carries at least two elements; in this run, because the
slice carried four elements, the contract was met and the result matched its safe
counterpart.

There is a reason these three methods exist in the language: safe `[]` access performs one
bounds-checking step on every call — it tests whether the index is smaller than the
slice's length, then reads. `get_unchecked` skips that step; the only thing it reads is an
index the caller has already verified somewhere else. This lesson does not count that
step's cost, because the run is limited to a single access and duration is not measured
anywhere in this curriculum; what matters is that which check was skipped can be read from
the source — the name `get_unchecked` says directly, without hiding it, that the bounds
check was skipped.

#### The Limiting Measurement

What these three operations share is that, used correctly, they leave no trace in the
result. In the run above, the raw read, the unchecked access, and the contracted call all
gave the same number as their safe counterparts — an observer looking only at the output
cannot tell which line was `unsafe`. The difference is not in the result, but in whose
responsibility it is what happens when they are used incorrectly.

Misuse is not run in this lesson, because a read that spills past the bounds is undefined
behaviour, and its result is specific to this machine, to this run — the next run might
not reproduce the same error, it might silently read a wrong value, or it might lead to a
crash. Describing it in prose is enough: a call to `pointer.add(100)` would try to read an
address that goes far past the four-element slice. The compiler does not reject this call
— because rejecting it would require knowing what the address points to, and it does not
know. In the same way, if `get_second_item` were called with a slice of fewer than two
elements, the contract would be violated, and the compiler would not notice this either;
the signature only assumes the caller has kept a promise, it does not test the promise.

This clarifies what `unsafe` checking does: the compiler no longer asks "is this correct",
it only asks "is this a form the source has permitted." Measuring where the boundary stops
is a separate matter from running what lies beyond it — this lesson does only the first of
those.

This distinction also explains why `unsafe` functions need a documentation convention.
Adding a documentation line above a function like `get_second_item` that spells out which
precondition is expected is not something the language requires — but it is a common
writing habit, because the `unsafe` word in the signature does not say by itself "which"
precondition, only that there **is** one. The function in this run carries that
documentation as a single-line comment; the Documentation lesson, later, will show that a
documentation line's own correctness is likewise not checked on its own, and falls into a
separate class (**nobody checks it**). What matters here is that `unsafe` itself does not
require this documentation, only that the language leaves a visible mark in the source:
without the `unsafe` word this function cannot be called at all, but the word's presence
does not show that the caller has actually passed a correct slice.

#### Summary

- An `unsafe` block does not close off the compiler's entire checking; it only excludes a
  few specific operations (raw pointer read/write, calling an `unsafe` function) from
  scope — a safe call inside the same block (`v.get`) stayed in the **still holds** class
- A read through raw pointer arithmetic, a slice's unchecked access method, and a call to
  a contracted `unsafe` function — all three are operations where checking is **handed to
  the writer**
- All three delegated operations, used correctly, gave a result identical to their safe
  counterpart; the difference is not in the result but in the responsibility for misuse
- The borrow checker keeps working inside an `unsafe` block too — it is not a checker that
  closes, only a few operations that open
- The signature of a function marked `unsafe` is a form that makes the precondition inside
  its body visible to the caller; the compiler does not test this precondition, it only
  forces its existence to be declared

#### Next Step

In this lesson, the delegated guarantee always stayed inside the same program — both the
raw pointer and the unchecked access were reading Rust's own memory layout; the compiler
always knew which type the pointer's target had and the slice's real length, it had only
stopped testing that knowledge at the moment of reading. The next lesson carries the same
delegation one step further: calling a function, written in another language, whose body
the compiler never sees at all. There, the contract is not only a precondition but a
signature — the compiler now compiles by trusting only the declaration written, without
ever looking at the other side's body.

### The Foreign Function Interface

Source: https://academia.sh/en/courses/rust-advanced/foreign-function-interface

Calling a C function whose body the compiler never sees, trusting only its declared signature; that the result of two foreign calls matches Rust's own counterpart is shown by running code, and that the correctness of the declaration is the writer's responsibility is explained.

The previous lesson showed the guarantee delegated to an `unsafe` function's signature:
`get_second_item`'s body was written in Rust, the compiler could read that body, it just
did not test the precondition on its own. This lesson carries the same delegation one step
further: calling a function whose body was not written in Rust, one the compiler never
sees at all. There, the contract is no longer a precondition sentence, only a signature —
the number of parameters, the parameter types, the return type.

This boundary exists for a practical reason: rewriting every function a program needs in
its own language is not mandatory. Calls provided by the operating system, long-used
numeric and text-processing libraries, hardware-specific drivers — a large share of these
are written in C, and C's binary compatibility convention serves as a shared boundary that
belongs to no single language. Rust does not reinvent this boundary; it offers a form that
can speak C's calling convention directly and communicates with the other side without
touching its body.

#### Calling by Trusting Only the Signature

Calling a C library function from Rust does not require seeing that function's source, or
even its having been compiled on the same machine — common C functions are already linked
into the operating system's own standard library. The Rust program only writes a
declaration: the function's name, the types of the parameters it takes, the type of the
value it returns. This declaration is placed inside an `extern "C"` block; the `"C"` word
says which binary compatibility convention (in what order the parameters are carried on
the stack or in registers) the call will follow — Rust's own functions can be compiled
with a different convention, and speaking to a C library requires this convention to be
explicitly switched to C's.

Once the declaration is written, no extra compile step or link step is required: the
compiler searches the system library it always links the program against, for these two
names, and finds them. Parameter types are not made up by hand either; the `std::os::raw`
module defines names like `c_int` and `c_char` that correspond to the width of C's `int`
and `char` types on this platform — these share the same memory layout as Rust's own `i32`
or `u8` types, but the naming makes it readable from the source that the declaration
matches the C side.

The boundary does not operate in only this direction. A Rust function can also be opened
outward with a signature that C can call; in that case the compiler leaves that function's
name unchanged so the linker can find it under the name C expects. This lesson measures
only the direction from Rust to C — `abs` and `strlen` are both provided by the system,
the side the Rust program calls. The reverse direction rests on the same contract
convention, only which side writes the declaration in the source changes.

The declaration itself and calling the declared function do not carry the same status.
Writing an `extern "C"` block resembles constructing the raw pointer measured in the
previous lesson: the compiler only records a name and a signature, it has not yet crossed
to the other side. The danger begins when there is a genuine jump to where that name
points — which is why only the call requires an `unsafe` block, the declaration itself does
not.

```rust
// declaration.rs — the declaration itself does not require unsafe, calling it does
use std::os::raw::c_int;

extern "C" {
    fn abs(x: c_int) -> c_int;
}

fn main() {
    // Carrying the function as a pointer does not call it; unsafe is not required.
    let fn_ptr: unsafe extern "C" fn(c_int) -> c_int = abs;
    println!("declaration compiled, not called: {}", fn_ptr as usize != 0);
}
```

```
declaration compiled, not called: true
```

This run never called `abs`; it only carried the declared function into a variable and
confirmed that this variable was not empty (did not point to address zero) — the real
address itself was never printed anywhere. **LL6.** Compilation gave no warning on this
line because the declared function was genuinely used, only not called; an unused
declaration would have been flagged as dead code by the compiler. The declaration itself
not requiring `unsafe` is another example of the distinction the language draws between
saying "this name points somewhere" and saying "I am jumping there now" — the same
distinction as between constructing a raw pointer and reading it in the previous lesson.

#### Measuring Two Foreign Calls

The run below calls two foreign functions: `abs`, which takes an absolute value, and
`strlen`, which counts the length of a byte array. Neither has a body inside this program
— only a declaration. The results are compared against Rust counterparts that do the same
job.

**LL7.** The number whose absolute value is taken is `-7`; the string whose length is
measured is `"merhaba"` (seven bytes, carrying no multi-byte characters).

```rust
// foreign.rs — the result of two foreign calls and their rust counterpart
use std::ffi::CString;
use std::os::raw::{c_char, c_int};

extern "C" {
    fn abs(x: c_int) -> c_int;
    fn strlen(s: *const c_char) -> usize;
}

fn main() {
    let abs_value = unsafe { abs(-7) };
    let text = CString::new("merhaba").unwrap();
    let length = unsafe { strlen(text.as_ptr()) };

    println!("foreign abs: {} (rust counterpart {})", abs_value, (-7i32).abs());
    println!("foreign length: {} (rust counterpart {})", length, "merhaba".len());
}
```

```
foreign abs: 7 (rust counterpart 7)
foreign length: 7 (rust counterpart 7)
```

Both calls sit inside an `unsafe` block — but for a different reason than the `unsafe` in
the previous lesson. There, the danger was a read spilling past the slice's bounds; here,
the danger is that the compiler knows nothing at all about the other side. The source code
of `abs` and `strlen` is nowhere in this program; the compiler produced the call without
ever seeing them, trusting only the two-line declaration above. **LL8.** The result
matched exactly on both lines: the foreign `abs` call returned `7`, and Rust's own `abs`
method also returned `7`; the foreign `strlen` call returned `7`, and Rust's `len` method
also returned `7`. The match is not a coincidence — it is the result of the declaration
matching the real C-side signature exactly.

The `CString::new` call here is a deliberate step: Rust's own string type does not carry a
zero byte at the end and keeps its length in a separate counter; C's string convention, by
contrast, is a byte array that ends with a zero byte. Calling `strlen` directly with a
Rust string would lead to a type mismatch at a boundary where the two conventions differ;
`CString` performs the translation between them, building a byte array that ends with a
zero byte, and passes only that array's starting address to `strlen`.

#### Separate Rules on the Two Sides of the Boundary

In this lesson, only single integers and byte arrays cross the boundary; if a struct
crossed the boundary, a further rule would come into play. Rust's own struct layout engine
can reorder fields however it likes during compilation — a freedom that does not change
the program's behavior, only affects its layout in memory. The C side, by contrast,
guarantees that it places a struct's fields one after another in declared order, following
alignment rules. Even if both sides agree on a struct carrying the same names, if one
reorders the fields while the other keeps the order fixed, the same memory block is read
with two different interpretations. This lesson does not pass a struct, only an integer
and a pointer; this part of the boundary is not measured here, it is only mentioned from
the source as something that exists.

`abs` and `strlen` were chosen because both only read memory — neither allocates a new
memory block and hands it over to the Rust side. This shows one of the most important
restrictions at the FFI boundary: if a C function allocated memory inside itself and
returned a pointer, who releases that memory, and with which allocator, would become a
separate matter of contract. Rust's own growable array and C's `malloc` function are not
required to use the same allocator; trying to release a memory block allocated on the C
side with Rust's own release rule mixes up the two separate allocators' ledgers, and that
produces undefined behaviour. This lesson does not run that situation — `abs` returns an
integer, `strlen` returns a length, neither allocates new memory — but it is mentioned to
make this part of the boundary's existence visible from the source.

Not every part of the boundary falls into the **handed to the writer** or **outside the
language** class; the `CString::new` call does not belong to either. C's
zero-byte-terminated string convention means a string cannot carry a zero byte inside it —
the string ends the moment a zero byte is seen. `CString::new` tests at run time whether
the byte array it is given carries a zero byte inside it, and if it does, stops the call
not with a panic but with an error value.

```rust
// zero_byte.rs — CString::new checks for a zero byte inside the body
use std::ffi::CString;

fn main() {
    let clean = CString::new("merhaba");
    let broken = CString::new("me\0rhaba");

    println!("clean string built: {}", clean.is_ok());
    println!("zero byte inside the body rejected: {}", broken.is_err());
}
```

```
clean string built: true
zero byte inside the body rejected: true
```

**LL9.** This is a different spot from the delegated part of the boundary in the `strlen`
call: here the checking is done not by the compiler but at run time, but it is still
**done** — the `Result` type gives the caller no permission to ignore the error. Not all of
the boundary is handed to the writer; an input that does not conform to C's own format
rule is stopped before it ever reaches the foreign call.

The way errors are reported also differs on the two sides of the boundary. In Rust, an
operation's failure is usually carried by a `Result` value, and the compiler checks
whether this value has been handled. C has no such type; a function's failure is usually
marked by a special return value (for instance, a negative number or zero) or by a
separate global state. An `extern "C"` declaration does not remove this difference — if the
declared return type is `c_int`, knowing whether that value is an error code or an
ordinary result still depends on the caller reading the C side's documentation; the
compiler cannot derive this distinction from the type, because the type is the same for
both.

Character encoding carries a separate trap concerning what the number `strlen` returns
means. Both Rust's own `len` method and `strlen` count the same thing: the string's
**byte** length — not how many characters the string visually consists of. Because the
`"merhaba"` used in this lesson consists only of single-byte characters, this distinction
did not show up; in a string carrying a character encoded with more than one byte, the two
numbers separate from each other.

```rust
// byte_char.rs — separating byte length from character count
use std::ffi::CString;
use std::os::raw::c_char;

extern "C" {
    fn strlen(s: *const c_char) -> usize;
}

fn main() {
    let text = "señor";
    let bytes = CString::new(text).unwrap();
    let foreign_length = unsafe { strlen(bytes.as_ptr()) };

    println!("rust byte length: {}", text.len());
    println!("rust character count: {}", text.chars().count());
    println!("foreign strlen: {}", foreign_length);
}
```

```
rust byte length: 6
rust character count: 5
foreign strlen: 6
```

**LL10.** `text.len()` and the foreign `strlen` call matched exactly here too (`6`) — both
count bytes. What comes apart is the character count found with the `chars().count()`
method (`5`): the `ñ` letter in the string, though written as a single character in the
Rust source, takes two bytes in UTF-8 encoding. A C side that relies on `strlen` always
reads this number as bytes; a caller that mistakes it for a character count has proceeded
with a wrong assumption on a string that is not single-byte. The compiler cannot catch this
wrong assumption either — the return type is `usize`, and `usize` does not say whether it
carries a byte count or a character count.

#### The Limiting Measurement

Both calls working correctly depended on the declaration having been written correctly —
and the compiler never tested this correctness at any point. `abs`'s real C signature
takes a single integer and returns a single integer; because the declaration wrote this
correctly, the call worked correctly. If the declaration had been written wrongly — for
instance, if `abs` had been declared to take two parameters or return a floating-point
number — the compiler would not have caught it. The compiler has no access to the other
side's body; it only looks at the declaration written and produces a call that matches
that declaration. The result of a wrong declaration is not run in this lesson, because its
result is specific to this machine and falls into the undefined behaviour class — values
on the stack can be interpreted as the wrong type, the program can produce a wrong number,
or it can crash.

This is where the **handed to the writer** and **outside the language** classes intersect.
Checking, as in the previous lesson, was handed to the writer — but what is handed over
here is not a precondition sentence, it is a signature carrying no guarantee at all about
the other side's correctness. The other side itself was written outside Rust, compiled
outside Rust's compilation process, and already linked by the system; the compiler's only
knowledge of it is the two lines the programmer wrote. If the declaration is correct, the
boundary between the two languages becomes invisible — the run above showed exactly this.
If the declaration is wrong, it is not the compiler but only an observation at run time
that can reveal where the error is.

The breadth of this boundary is measured not by a single wrong declaration but by every
field of the declaration. If the parameter count is wrong, the call shifts the positions
it reads from the stack or registers, and every parameter after it carries a wrong value
too. If a parameter type is not the correct width — for instance, if the real C signature
expects a four-byte integer while the declaration writes an eight-byte integer — the
caller ends up sending extra bytes the other side never reads, or sending too few. If the
return type is wrong, the bytes the call returns are interpreted according to the wrong
type. What all three cases share is that the compiler rejects none of them: an
`extern "C"` block is a syntactically valid declaration, and whether it matches the real
signature on the C side is not compilation's concern.

#### Summary

- An `extern "C"` block declares a function whose body is written outside Rust using only
  its signature; the compiler never sees that body, and its guarantee stays entirely
  **outside the language**
- Common C library functions are already linked on the system; once the declaration is
  written, no extra compile or link step is required
- The result of two foreign calls (`abs`, `strlen`) matched Rust's own counterpart exactly
  — the match is the result of the declaration having been written correctly, matching the
  real C signature
- `CString` closes the convention gap between Rust's length-counted string and C's
  zero-byte-terminated array with an explicit conversion at the boundary
- If the declaration had been written wrongly, the compiler could not have caught it;
  correctness is entirely the writer's responsibility — this is the point where the
  **handed to the writer** and **outside the language** classes intersect
- Not every part of the boundary is handed over: `CString::new` tests at run time whether
  there is a zero byte inside the body, and if there is, stops with an error value — this
  check, even though it happens at run time, still **holds**

#### Next Step

In this lesson, the boundary was between two languages; as long as the declaration was
correct, which machine it ran on did not matter — the `abs` and `strlen` calls produced the
same result no matter which processor or operating system the program ran on, because both
were linked to the system's own standard library. The next lesson reverses this
independence: the same source needing a different body depending on the **target** it runs
on. There, the guarantee will not rest on a declaration but on the constraints the target
itself gives — and that constraint will be written not in an external library's signature,
as in this lesson, but conditionally, in the source itself.

### The WebAssembly Target

Source: https://academia.sh/en/courses/rust-advanced/webassembly-target

The same source needing a different body depending on the target it runs on: a body that relies on a part of the standard library (the file system) and one that does not are separated with conditional compilation, both are run on the local target, and both give the same result.

The previous lesson measured the case where the boundary sat between two languages: as
long as the declaration was correct, it did not matter which processor or operating system
it ran on. This lesson reverses that same independence. Here the language stays the same —
the entire source is Rust — but the **target** it runs on changes, and some bodies cannot
stay indifferent to that change. A target running in a browser does not always have access
to a file system, an operating system thread, or a clock; this lesson measures how that
access is made **visible** in the source.

The boundary in the previous lesson rested on a declaration's correctness; the boundary
here rests on the environment the target itself provides — both rest on an assumption the
compiler cannot test directly. This constraint does not belong to Rust; it comes from the
browser's own security model. A browser tab keeps the code it runs in a space isolated
from disk, network, and operating system; a page opening a file on its own, creating a
thread, or establishing a socket runs against the purpose of that isolation. A program
compiled into this space is limited to the capabilities that space offers — and these
capabilities are narrower than what the same program's version running on a server or a
desktop has. Rust's standard library does not hide this difference: part of modules such
as the file system, threads, and time either do not exist at all in this isolated target,
or behave differently.

In this lesson, only the local target is compiled. No real compilation is done for another
target; the target-dependent decision is shown on the **local** target with conditional
compilation — which of the two bodies in the source is selected on this machine is seen by
running code, while the other body sits in the source without ever being compiled. This
selection is not made at run time: the decision managed by `cfg` is made during
compilation, before the program starts running. It is not possible to pick one of the two
bodies inside the same binary and switch to the other at run time — whichever target it
was compiled for, that target's body enters the binary, the other never does.

#### A Body Split by Target

`#[cfg(...)]` is not a run-time branch; it should not be confused with an `if` expression.
`if` tests a condition at run time, in a structure where both branches sit in the binary
and only one is executed. `cfg`, by contrast, decides at compile time: the item that does
not satisfy the condition never enters the rest of compilation at all, as if it had been
cut from the source tree entirely. The result of this difference is seen in the run below.

The run below defines two bodies that read a sensor record. One reads from the file
system; the other stands in for a body that would be compiled on the browser target, and
it is never compiled on this machine at all. Which body gets compiled is decided by the
`#[cfg(...)]` attribute.

**LL11.** The sensor record is a four-line text: `12`, `7`, `5`, `21` — summing to `45`.

```rust
// wasm1.rs — a body split by target, the branch selected on the local target
use std::fs;

#[cfg(not(target_family = "wasm"))]
fn read_records() -> String {
    // Body relying on the file system: compiled only on targets that have one.
    fs::read_to_string("sensor.txt").expect("could not read file")
}

#[cfg(target_family = "wasm")]
fn read_records() -> String {
    // This body is compiled on the browser target; it is never compiled on the
    // local target, so an undefined call carried here breaks nothing here.
    body_reads_from_another_source_on_the_wasm_target()
}

fn total(text: &str) -> i32 {
    text.lines().filter_map(|s| s.trim().parse::<i32>().ok()).sum()
}

fn main() {
    fs::write("sensor.txt", "12\n7\n5\n21\n").expect("could not write file");
    let content = read_records();
    println!("file-system-backed body total: {}", total(&content));
}
```

```
file-system-backed body total: 45
```

There are two `read_records` definitions in the source, but the compiled program contains
only one. The body marked `#[cfg(not(target_family = "wasm"))]` entered compilation
because this machine is not part of the wasm family; the body marked
`#[cfg(target_family = "wasm")]` was never compiled at all — even though the call inside
it is defined nowhere, the compiler never noticed, because it never saw that body.
**LL12.** This shows that a body left out by `cfg` passes through no semantic checking at
all, as long as its syntax is valid; "not compiled" and "wrong" are not the same thing —
the compiler passes no judgment at all about a body that is not compiled.

This may look similar to the foreign function declaration in the previous lesson, but it
carries a different guarantee. An `extern "C"` declaration described a body the compiler
never saw but which genuinely existed and had to be linked; if the linker could not find
that name, compilation broke. A body marked `#[cfg(target_family = "wasm")]`, by contrast,
is never looked for on this machine at all — it does not matter that the name
`body_reads_from_another_source_on_the_wasm_target` inside it is defined nowhere in this
program, because that name never reaches the linker. In the foreign function interface,
what was delegated was a declaration's correctness; here nothing is delegated, because
that body is not even part of this compilation.

The `.expect(...)` call inside `read_records` is also a deliberate choice: the read is
known not to fail, because the file was written one line earlier inside the same `main`
function, but Rust cannot know this at compile time — `read_to_string` always returns a
`Result`, and this value has to be handled. `.expect(...)` unwraps that value, choosing to
stop with a panic on failure; because this lesson never triggers a panic, this path is only
ever followed in the success case.

The run above chose the form `target_family = "wasm"`; one of the attribute names the
language recognizes is also `target_arch = "wasm32"`, and the two do not ask the same
question. `target_family` asks about the broad family a target belongs to (the wasm
family, the unix family); `target_arch` asks about the processor architecture's name
(`wasm32`, `x86_64`). This lesson asked about the family, because the distinction being
measured rested not on architecture but on the class of environment the target provides —
a target without a file system is not limited to a single architecture. Which attribute
name is chosen is a decision readable from the source, and it says which question the body
is split by.

#### The Counterpart That Does Not Rely on the File System

A body being split by `cfg` does not mean that body has to keep existing. The same sensor
record can also be obtained without ever touching the file system — for this lesson, the
value is embedded inside the source. The difference between these two options is not an
implementation detail: the first assumes the data comes from a source at run time, the
second assumes the data is already known at compile time. Which assumption is valid
depends on the problem being solved itself — if the sensor record is genuinely changing
data, a file (or another source standing in for it) is needed; if it is a fixed
configuration value, an embedded constant is enough.

```rust
// wasm2.rs — a body that never relies on the file system, independent of target
const EMBEDDED_RECORDS: &str = "12\n7\n5\n21\n";

fn total(text: &str) -> i32 {
    text.lines().filter_map(|s| s.trim().parse::<i32>().ok()).sum()
}

fn main() {
    let total_value = total(EMBEDDED_RECORDS);
    println!("embedded body total: {}", total_value);
}
```

```
embedded body total: 45
```

Both bodies gave the same result (`45`), but by different routes. For the
file-system-backed body to work, the environment had to provide a file system — it does on
this machine, but on a browser target this assumption generally does not hold, because a
browser page cannot access a file system directly. The embedded body, by contrast, depends
on no external source at all; its data is fixed inside the source itself, at compile time.
**LL13.** These two forms, producing the same result, show a target-independent task
(summing four numbers) being fed from a target-dependent source (a file) and a
target-independent source (an embedded constant).

Embedding the data into the source here was done with a `const` declaration; the value
becomes part of the binary during compilation and requires no external source at run time.
This form is not a solution limited to the browser target alone — the same approach is
used for environments where a file system does not exist at all, where only a fixed memory
layout exists. The next lesson will take up that environment directly; the constraint
there is not the browser's isolated space but the absence of the standard library itself.

#### A Second Split: Threads

The file system is not the only example. The same pattern also applies to the capability
of opening multiple threads — code a browser page runs does not always have access to the
operating system's ordinary thread-creation call. Opening a thread asks the operating
system to allocate a new unit of execution and schedule it; this is a different service
from opening a file but a request from the same class — neither can be carried out from
inside the source alone, both wait for the environment to provide something. The run below
sets up the same `cfg` pattern once more: the body compiled on the local target opens a
thread and collects its result, while the body written for the wasm family is again never
compiled on this machine at all.

**LL14.** The computed value is `6 * 7`, that is `42`; the value the thread returns is
collected into the main body with a `join` call.

```rust
// wasm3.rs — a body relying on a thread, the branch selected on the local target
use std::thread;

#[cfg(not(target_family = "wasm"))]
fn produce_value() -> i32 {
    // Body that opens a thread: not compiled on targets without multithreading support.
    let worker = thread::spawn(|| 6 * 7);
    worker.join().expect("the thread cannot have crashed")
}

#[cfg(target_family = "wasm")]
fn produce_value() -> i32 {
    computed_by_a_separate_method_on_the_wasm_target()
}

fn main() {
    println!("thread-backed body: {}", produce_value());
}
```

```
thread-backed body: 42
```

In this run too, the difference sits in the same place: the `thread::spawn` call asks the
operating system to open a separate thread, and this request cannot be satisfied on every
target. The `#[cfg(not(target_family = "wasm"))]` label marks the body carrying this
dependency; a body performing the same calculation directly, without opening any thread,
could also have been written (`6 * 7` computed on a single line), and that body would work
identically on every target. The point here is the same as the previous measurement: what
depends on the target is not the calculation itself, but **how** the calculation is
carried out.

There is also a difference between these two examples (file, thread): the absence of the
file system usually shows itself with an error value — the call returns a `Result`, and if
it fails, this is understood through that value. The state of the thread-opening
capability changes by target; on some targets the function does not exist at all, and the
source, written using that function, does not compile; on other targets it may work under
a different restriction (for instance, limited to a single thread). What is common in both
cases is that this difference has to be visible somewhere in the source — either through a
`cfg` label, or through how the returned value is handled.

#### The Limiting Measurement

Both runs above gave the same result on this machine — but that does not mean the
file-system-backed body will work on every target. The source itself already says which
part of it is target-dependent: the `#[cfg(not(target_family = "wasm"))]` label declares
that the body under it will be compiled on targets outside the wasm family, and the
`#[cfg(target_family = "wasm")]` label declares that the opposite body will be compiled
only on the wasm target. Reaching this information does not require running a program on
that target; the label itself is a declaration that can be read without looking at any
run.

The reverse also holds: a body carrying no `cfg` label does not automatically mean it will
work on every target. The `total` function carries no `cfg` in this lesson and is shared
by both bodies — because it only parses and sums text, it touches no external source. But
if a function carried a call that opened a thread, read the real clock, or established a
socket while carrying no `cfg`, this dependency would not be visible from the source — the
compiler does not reject such a call, it either gives a compile error when compiling for
that target, or produces an unexpected result at run time. Source looking portable does
not show that it is portable; which assumption is target-dependent can only be read from
the `cfg` labels in the source and the standard library parts used, not from a single run.

The two examples measured in this lesson (file system, thread) are not an exhaustive list,
they are two faces of the same boundary. Reading the real clock falls into a similar
class: a target may not have access to the system clock, or that access may be provided by
a different route. What the three examples share is that all of them ask a service from
the operating system or the hardware — the file system asks for a storage service, the
thread asks for a scheduling service, the clock asks for a hardware counter service. An
isolated target either does not offer some of these services at all, or offers them
through a different interface; the source stays readable to the extent that it marks which
function asks for which service, with `cfg` labels or with the module itself that is used.
All three are not run in this lesson; only the file system and thread examples were run,
the clock example is only mentioned because it belongs to the same class.

This readability is also why real multi-target source (libraries expected to compile
across several operating systems and architectures) tends to look full of `cfg` labels;
the two examples built throughout this lesson are nothing more than a small-scale model of
the patterns in that kind of source. A reliable method before porting source to a target
is to scan the source for `cfg` labels and calls that ask for an external service — that is
exactly what is done in this lesson, only across a single pair of targets (the local
target and the wasm family) and a single body.

#### Summary

- A target-dependent decision can be split into two separate bodies in the source with
  `#[cfg(...)]` attributes; only one is compiled on the local target, the other sits in
  the source without ever being compiled — `cfg` does not branch at run time like an `if`,
  it decides at compile time
- No real compilation was done for the browser target; the target-dependent decision was
  **modelled** through the source
- The body relying on the file system and the body that does not gave the same sensor
  total (`45`); the difference is not in the result but in which environment assumption
  they rest on
- A browser target not offering some platform capabilities, such as a file system, shows
  that this target's guarantee comes not from the language itself but from the target —
  the class is **outside the language**
- The file system and opening a thread are two separate instances of the same boundary:
  both ask a service from the operating system, and that service is not offered the same
  way on every target
- Source looking portable does not mean it is portable; target dependency is read from the
  `cfg` labels and the standard library parts used, from the source — not from a run

#### Next Step

In this lesson, the boundary sat between two targets of the same language; the difference
was which capabilities the environment offered — both the file system and threads rested
on the assumption that an operating system exists, only whether that assumption was met
changed from target to target. The next lesson carries this boundary to an extreme: an
environment where the standard library does not exist at all, where the operating system
itself is absent. There, even a target-independent form like an embedded constant will run
into the question of whether memory allocation itself exists at all — in this lesson,
"target-dependent" meant only a few functions; there, "target-dependent" is a constraint
that determines how the program's entire body is written.

### Embedded Targets

Source: https://academia.sh/en/courses/rust-advanced/embedded-targets

How a target where the operating system services underlying the standard library (printing, memory allocation, panic handling) do not exist at all is made visible in the source; that two bodies doing the same job, one by allocating and one with a fixed-size buffer, give the same result is shown by running code.

The previous lesson measured the same operating system offering different services on two
separate targets: the file system and threads either did not exist at all on the browser
target, or arrived through a different route. This lesson carries the boundary to an
extreme — a target where the operating system does not exist at all. On such a target, it
is not just a few services but the very foundation the standard library rests on that
changes: printing, memory allocation, and panic handling — all three assume an operating
system exists.

A target of this kind typically represents hardware with limited memory that does not run
an operating system of its own: a sensor board, a motor controller, a card payment device.
On hardware like this, code runs directly on the processor; underneath it there is no
layer that manages a file system, organizes memory allocation, or prints a message to a
terminal when a program crashes. For this environment, Rust does not offer the entire
standard library, only a narrower library that supplies the core language and
hardware-independent basic types (integers, slices, option and result types); parts that
depend on the operating system, such as files, threads, networking, and memory allocation,
are left out of this narrow library. This distinction is marked in the source with a
declaration: this lesson does not use that declaration, because source that uses it cannot
be run as an ordinary program on this machine — the declaration says that no call
remaining requests the whole standard library, otherwise compilation stops. The
distinction measured in this lesson can be read from the source even without that
declaration being written: which body relies on which service is evident from the call it
uses.

In this lesson too, no real compilation is done for an embedded target. The
target-dependent decision, as in the previous lesson, is modelled through the source:
`cfg` determines which of the two bodies in the source is selected, and only one enters
compilation.

#### Printing Being an Operating System Service

A print call runs an operating system call behind it: it writes text to a stream, and the
stream is directed by the operating system to a terminal or a file. On a target where the
first link in this chain — the operating system itself — is absent, the same call cannot
write anywhere. In the previous lesson, the absence of a service (the file system) came
back as an error value — the program still compiled, and the absence was understood at run
time through a `Result`. The print call's situation here is different: in the narrow
edition of the standard library, this call itself does not exist at all, so its absence is
not a run-time result but a **compile-time** one — source that contains the call never
compiles at all for that target. The run below marks this dependency with `cfg`.

**LL15.** The printed message is `"measurement complete"` — reporting that a stage
assumed to process the previous lesson's sensor record has finished.

```rust
// embedded1.rs — printing being dependent on the operating system
#[cfg(not(target_os = "none"))]
fn print_info(message: &str) {
    // Writing to the standard output stream: a service the operating system provides.
    println!("{}", message);
}

#[cfg(target_os = "none")]
fn print_info(message: &str) {
    body_writes_a_different_way_on_the_embedded_target(message)
}

fn main() {
    print_info("measurement complete");
}
```

```
measurement complete
```

`target_os = "none"` is the value carried by targets without an operating system; because
this machine runs under an operating system, it does not satisfy this condition, and the
body marked `not(target_os = "none")` enters compilation. The opposite body, as in the
previous lesson's wasm example, sits in the source without ever being compiled — the
undefined call inside it therefore causes no problem at all. **LL16.** On a target that
genuinely has no operating system, for this second body to do the printing job it needs an
entirely different method, such as writing directly to a hardware register or sending data
to a debug interface — this method is independent of the stream abstraction the operating
system provides.

`target_os = "none"` asks a different question from the `target_family = "wasm"` used in
the previous lesson. `target_family` asks whether a target belongs to a broad environment
class (the wasm family, the unix family); `target_os`, by contrast, asks a narrower
question and names which operating system the target runs on, or whether it runs on none
at all. The value `"none"` is the single case where the answer to this question is "none"
— underneath the target there is no file system, process management, or stream-providing
layer. The same attribute can also carry other values (such as an operating system's
name), but this lesson concerns itself only with the "no operating system at all" case.

The difference between these two bodies is built in the same way as the file system
example in the previous lesson, but the service it rests on differs. There, what was
missing was a single call; here, what is missing is the stream abstraction itself that
underlies every print call in the standard library. A print call actually sits at the top
of a chain of layers: it formats the text, hands the result off to a function that writes
to the standard output stream, and that function in turn uses a write call the operating
system provides underneath. If the last link in this chain is missing, the rest of the
chain becomes meaningless too — which is why this print path does not appear directly in
the narrow library prepared for a target without an operating system.

#### The Same Job, Allocating and Not Allocating

Memory allocation carries a similar dependency too, but it is clearer to show the
difference not with `cfg` but with two different forms: both forms compile and run on this
machine, the difference between them is only that one allocates memory and the other does
not. In the print example, only one of the two bodies was compiled; here, both are
compiled and both run, because what is measured is not which one is compiled but which
source each relies on to produce the same result. The run below collects the same sensor
record in two separate ways.

```rust
// embedded2.rs — an allocating body and a non-allocating body giving the same result
fn sum_by_allocating(records: &[i32]) -> i32 {
    let mut accumulator: Vec<i32> = Vec::new();
    for &value in records {
        accumulator.push(value);
    }
    accumulator.iter().sum()
}

fn sum_with_buffer(records: &[i32]) -> i32 {
    const CAPACITY: usize = 8;
    let mut buffer = [0i32; CAPACITY];
    let mut filled = 0usize;
    for &value in records {
        buffer[filled] = value;
        filled += 1;
    }
    buffer[..filled].iter().sum()
}

fn main() {
    let records = [12, 7, 5, 21];
    println!("allocating total: {}", sum_by_allocating(&records));
    println!("buffer total: {}", sum_with_buffer(&records));
}
```

```
allocating total: 45
buffer total: 45
```

**LL17.** Both totals are `45` — the same four numbers, the same result.
`sum_by_allocating` widens the growable array's capacity as needed on every `push` call;
this widening results in requesting a new block from a memory allocator, and the allocator
is part of the runtime. `sum_with_buffer`, by contrast, touches no allocator at all: the
`buffer` array's size is predetermined in the source with the `CAPACITY` constant, the
array is as short-lived as a single function call, and it sits in a region whose place in
memory is known at compile time.

On a target without a memory allocator, `sum_by_allocating` either does not compile, or,
even if it does, it cannot proceed at run time because the allocation request cannot be
satisfied; `sum_with_buffer`, by contrast, produces the same result even on such a target,
because it needs no external source at all. This difference does not show up in the run —
both worked without issue on this machine — it shows up only in the choice, in the source,
between `Vec` and a fixed-size array.

This choice is not limited only to whether an allocator exists. On hardware with little
memory, such as a sensor board, knowing in advance the most memory a program will ever use
carries a separate value: because `buffer`'s size is fixed in the source, the amount of
memory this function will use is known at compile time and depends on no run-time decision
at all. The memory used in the `Vec`-based form changes with the number of incoming
records; if the record count is not known in advance, the upper bound to be used is not
known in advance either. The constraint a fixed-size buffer brings — that the upper bound
is written in the source — is also the source of this predictability.

#### The Limiting Measurement

There is one condition for the fixed-size buffer to do this job: `CAPACITY` must be
greater than or equal to the number of incoming records. In the run above, four records
fit into a buffer with a capacity of eight. If the record count exceeded the capacity and
the code did not check for this, the line `buffer[filled] = value` would try to write past
the array's bounds — because bounds checking always runs in Rust, this does not pass
silently, the program would stop at run time. Because this lesson does not trigger a
panic, it does not run that situation; instead, it runs a form that handles the same
boundary without panicking.

**LL18.** Ten records are sent, capacity is eight; the first eight are accepted and
summed, the last two are rejected.

```rust
// embedded3.rs — a buffer that rejects input past capacity without panicking
fn sum_with_buffer_safe(records: &[i32]) -> (i32, usize) {
    const CAPACITY: usize = 8;
    let mut buffer = [0i32; CAPACITY];
    let mut filled = 0usize;
    let mut rejected = 0usize;
    for &value in records {
        if filled < CAPACITY {
            buffer[filled] = value;
            filled += 1;
        } else {
            rejected += 1;
        }
    }
    (buffer[..filled].iter().sum(), rejected)
}

fn main() {
    let many_records = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
    let (total, rejected) = sum_with_buffer_safe(&many_records);
    println!("accepted total: {} rejected records: {}", total, rejected);
}
```

```
accepted total: 36 rejected records: 2
```

This form never crosses the boundary, by combining the `filled < CAPACITY` check with the
write in the same operation — the ninth and tenth records are counted and set aside
without ever being written to the buffer at all. The result is information instead of a
panic: how many records were accepted, how many were rejected. **LL19.** This shows a
common habit on an embedded target: because there is no operating system to catch a crash
and print the message, checking the boundary by hand in advance and reporting the state
with a result value is accepted as a more reliable form than stopping with a panic — even
though post-panic behavior changes by target, a result value can be handled the same way
on every target.

`sum_by_allocating` carries no such boundary — `Vec` grows itself when its capacity fills,
and it works even when the record count is not known in advance. This shows the real
trade-off between the two forms: the fixed-size buffer does not require the target to
provide an allocator, but its upper bound has to be written in the source, and when that
bound is exceeded, it either stops or (if written by hand as above) rejects the excess. The
growable array, by contrast, carries no upper bound, but it requires an allocator to exist.
A non-allocating form is not always possible; in a job where the record count is learned
at run time and cannot be bounded in advance, a fixed-size buffer has to either reject the
excess or stop.

In a real embedded system, `CAPACITY` is usually not chosen arbitrarily; it is tied to a
number that comes from the hardware itself — how many sensor channels a board has, how
many bytes a communication frame carries at most, the size of the memory region a buffer
fits into. The `8` chosen for this lesson represents such a hardware constraint; the
ten-record input was chosen to show a situation where that constraint is exceeded. Instead
of silently discarding the input that exceeds it, the code reports it by counting how many
were rejected — another habit commonly seen in embedded software: passing a boundary
without noticing it is a worse outcome than noticing the boundary was passed and recording
that fact somewhere.

Panic behavior itself is also part of this boundary. Under an operating system, a panic
prints a message and stops the program; in some configurations the program also tries to
unwind from that point, in others it stops directly — but in both cases this behavior rests
on the process management an operating system provides. The message is written to a
stream, the process returns to the operating system with an exit code, and the operating
system closes that process. On a target without an operating system, none of the links in
this chain exist; the narrow edition of the standard library does not specify which
behavior is followed at the moment of a panic, it only requires that this behavior be
defined separately by the source. The writer can choose, in this definition, to write a
fixed value to a hardware register, to put the processor into an infinite loop in a
low-power mode, or to send a signal to a debug interface; which one is chosen depends on
the hardware itself and which recovery routes that hardware offers. This lesson does not
run such a definition, it only mentions its existence — because it is a dependency
belonging to the same class as printing and allocation.

What the three dependencies (printing, allocation, panic handling) share is that none of
them is missing from the language itself. Rust recognizes all three at the language level;
the print syntax, the `Vec` type, the panic keyword — all of them are part of the core
language or the standard library. What is missing is the environment these three need in
order to work — a stream, an allocator, process management. The language stays the same,
the environment the target provides changes; that is why the class of these three
dependencies is **outside the language**, not a deficiency of the compiler or the standard
library.

#### Summary

- Printing, memory allocation, and panic handling are the standard library's three basic
  services that depend on the operating system; on a target without an operating system
  (`target_os = "none"`), all three have to be handled separately in the source
- No real compilation was done for an embedded target; the target-dependent decision was
  **modelled** through the source with `cfg`
- The allocating body (`Vec`) and the body working with a fixed-size buffer gave the same
  total (`45`); the difference is not in the result but in one of them requiring an
  allocator
- A target not offering a memory allocator, an operating system service, or a panic
  handler shows that the guarantee this target gives comes not from the language itself
  but from the target — the class is **outside the language**
- A fixed-size buffer requires no allocator, but its upper bound has to be written in the
  source; when this bound is exceeded, the non-allocating form either stops or (if the
  bound was checked by hand) rejects the excess without panicking

#### Next Step

Throughout this topic, the guarantee always ended in the same place: not the compiler's
checking, but a mechanism outside the language was what determined it. In the `unsafe`
Rust lesson, that mechanism was a contract delegated to the writer; in the Foreign
Function Interface lesson, it was the signature of a language the compiler never sees; in
the WebAssembly Target lesson, it was an environment isolated by the browser; in this
lesson, it was the absence of the operating system itself. What all four share is that the
boundary of the guarantee is readable from the source — the `unsafe` block, the `extern`
declaration, and the `cfg` label are all three marks that flag this boundary in the
source; none of them is a hidden behavior that has to be discovered during a run. The next
topic departs from this axis: what it asks there is which guarantee a dependency or a test
gives, not what a target takes away.

## Project Lifecycle

Setting up a modular library with conditional compilation, the guarantee given by unit and integration tests with random input generation, verifying performance claims with counts, runnable examples of documentation generated from source, and what a version promises.

### Dependencies and Feature Flags

Source: https://academia.sh/en/courses/rust-advanced/dependencies-and-feature-flags

That a piece of source excluded by conditional compilation is never seen by the compiler at all, confirmed by repeating the same observation with two separate feature flags to establish the class (nobody checks it).

The Low-Level and Interoperability topic closed with four lessons, and in all four the
compiler still saw at least something: a few operations inside an `unsafe` block stayed
unchecked, but another operation in the same block was still checked; a foreign function's
body was never seen, but at least its signature entered compilation; the limit a target
imposes was read from the source, and two versions — one relying on the standard library,
one not — ran side by side on the native target. In all four, the compiler compiled *some
part* of the source, and the guarantee started from there — the checking narrowed, but it
never stopped entirely. The Project Lifecycle topic asks a completely different question: is
there a piece that sits in the source but that the compiler never sees at all, and if so,
does anyone notice even if that piece breaks?

This lesson looks for the answer in conditional compilation. A section of a source file
enters compilation or does not, depending on whether a feature flag is on; a section that
does not enter passes through no checking at all, beyond its syntax merely having to be
valid. The Rust Ecosystem and Advanced Topics course's shared question — where does the
compiler's guarantee end here — finds its sharpest answer in this lesson: for some bodies,
the guarantee never starts at all.

The course's four answers — the guarantee **still holding**, being **handed to the
writer**, falling **outside the language**, or ending up where **nobody checks it** — each
find their counterpart, one by one, in the Project Lifecycle topic's four lessons: this
lesson shows a body that is never checked at all, the next two show which guarantee a test
or a count establishes *by running*, and the last lesson shows that the same source can hold
two claims side by side — one never checked, one genuinely checked.

#### The Body Left Out of Scope

A manifest file normally declares, through feature flags, which parts of a crate are
optional; the package manager passes these flags to the compiler with the `--cfg` option.
Since this course does not use a package manager, the flag state is given directly to the
compiler, but the mechanism is identical: an item marked with the `#[cfg(feature = "name")]`
attribute enters compilation only while that flag is on. While the flag is off, the compiler
**never sees** that item at all — it neither runs it through type checking nor resolves its
names.

Dependencies and feature flags are usually mentioned together because a flag is frequently
used to limit the need for a dependency: if a section relies on a particular piece of
external functionality that most users do not need, that section is placed behind a flag,
and while the flag is off, neither the section nor the dependency it relies on ever enters
compilation. This lesson does not use a dependency outside the standard library —
dependencies stay limited to the standard library throughout the course — but the mechanism
is identical: `#[cfg(...)]` leaves an item out of compilation regardless of what it depends
on.

Feature flags are mostly used to separate a crate's optional sections: a section that most
users do not need is placed behind a flag, and only the party that wants it turns that flag
on. A manifest file also states which of these flags come on **by default**; unless
explicitly turned off, default flags enter every build, and the rest are turned on only when
the requesting party reaches for them. The payoff of this is that the unused section is
never compiled at all — but what this lesson measures is not the payoff, it is this payoff's
**cost**: the section that stays off lives at a different level of checking than the section
that stays on.

The section below carries two bodies marked with two flags. The first sits under the
`unopened` flag and carries two different errors: it calls a function that does not exist
and assigns a string directly to an integer. The second sits under the `untried` flag and,
in a similar way, calls a method that does not exist and assigns its result to the wrong
type. Both bodies were deliberately built to violate two different kinds of static checking:
one violates **name resolution** (a call that does not exist), the other violates **type
checking** (a wrong assignment). Both were needed to show that they arrive at the same
outcome — silently staying out — because choosing only one kind of error would have let the
observation apply to only that error class. The third body is not tied to any flag and
enters compilation under every condition.

```rust
// flag_bodies.rs — two separate flags that always stay off, one body that always stays on
#[cfg(feature = "unopened")]
pub fn never_checked() {
    this_function_was_never_defined();
    let _x: i32 = "text";
}

#[cfg(feature = "untried")]
pub fn another_unchecked(list: &[i32]) -> i32 {
    let total: i32 = list.parallel_sum();
    let _s: &str = total;
    total
}

pub fn running_body() -> i32 {
    41
}
```

This file carries no `fn main` at all; it stands as a source section, and the body below
pulls it in with a `mod` declaration. **PL1.** The claim here is this: when no flag is on,
compilation finishes **without warnings** even though the two faulty bodies inside
`flag_bodies.rs` remain in the source — because neither one ever enters compilation.

```rust
// flag_measurement.rs — both flags off; the two excluded bodies are never checked at all
mod flag_bodies;
use flag_bodies::running_body;

fn main() {
    println!("running body result: {}", running_body());
    println!("is the unopened flag on: {}", cfg!(feature = "unopened"));
    println!("is the untried flag on: {}", cfg!(feature = "untried"));
}
```

```
running body result: 41
is the unopened flag on: false
is the untried flag on: false
```

Compilation printed no warning at all, and the program wrote three lines. Both
`never_checked` and `another_unchecked` remain in the source, each carrying an undefined
call and a type error, and neither was **compiled at all** in this run. Because the compiler
never saw them, it could catch neither the call nor the faulty assignment. A runtime flag
query inside the code confirms this from within the program: both flags read as off during
the run — both off, both bodiless.

This carries an important distinction for not confusing a flag with an ordinary runtime
condition. If the same two faulty bodies had been written inside an ordinary runtime branch
instead of `#[cfg(...)]` — say, a condition that is never true — the outcome would not be
the same: even if such a branch's body never runs at runtime, it still **passes type
checking** at compile time, because it remains present in the source tree. If an undefined
call were written there, compilation would fail — a branch that is never reached at runtime
does not hide an undefined name. `#[cfg(...)]` does exactly the opposite: it removes the
body from the source tree entirely, and with it from both the name-resolution and
type-checking stages. **PL2.** This difference shows that "code that never runs" and "code
that is never compiled" are not the same thing; only the latter falls into this lesson's
class, the nobody-checks-it class.

**Distinguishing this from a related idea:** the Production Go: Testing, Tooling and
Performance course measured build tags as a program's **tool-level knowledge** — a build tag
decided which file compiled for which operating system or architecture, and it was a tool
that reported this. What this lesson measures is entirely different: that the excluded body
is **never checked at all**. There, the question was whether a piece of knowledge came from
the source, a tool, or a run; here, the question is whether a part of the source ever enters
the compiler's sphere of interest at all. Both ideas use the `#[cfg(...)]` attribute — in
Rust, target selection and feature selection both go through the same mechanism — but they
ask different questions: one asks "which file compiles for where," the other asks "who
checks a body that is never compiled."

The same observation was repeated with a second flag (`untried`) because a single flag
staying off could have been a coincidence — maybe that flag was designed never to be
used at all. **PL3.** Two different flags, carrying two different kinds of error (an
undefined call with a wrong type assignment, then an undefined method with a wrong type
assignment), reached the same outcome: a closed flag's body, whatever it contains, never
enters the compilation process at all. This is not a special case of a single example; it is
conditional compilation's general behavior.

A project can reduce this risk by delegating it to a process that compiles every flag
combination separately: if every combination is compiled at least once, no flag stays off at
that moment, and checking momentarily reaches full coverage. This does not remove the
measured class from **nobody-checks-it** — it only defers the moment of checking to the
moment the flag is turned on. Every moment such a process is not run, the bodies behind the
source tree's closed flags keep staying unchecked; what this lesson measures is what happens
in that process's **absence**, not what is guaranteed in its presence.

#### A Bounding Measurement

The run above showed that a closed flag's body does not enter compilation. But that does not
mean the body is *invisible* — it sits in the source, it can be read, it can be tracked in
the version control system. What is invisible is whether the compiler looks at it. The run
below shows this distinction from within a running program: a block set aside for a closed
flag's body is never executed by the program itself, either.

```rust
// body_visibility.rs — a closed flag's body is invisible from the run, too
mod flag_bodies;
use flag_bodies::running_body;

fn main() {
    let result = running_body();
    #[cfg(feature = "unopened")]
    {
        println!("this line never compiles");
    }
    #[cfg(not(feature = "unopened"))]
    {
        println!("unopened flag is off: its body stays in the source, never entered the run");
    }
    println!("running body returned {} again", result);
}
```

```
unopened flag is off: its body stays in the source, never entered the run
running body returned 41 again
```

**PL4.** Both blocks are written inside the same `main` function, but only one enters the
run — which one enters is decided at compile time, by looking at the `cfg` attribute. Both
blocks exist in the source; only one exists in the run. This is not a promise conditional
compilation makes to the reader, it is a fact that can be derived from the source itself —
but this measurement itself carries a limit.

In this run, `#[cfg(...)]` sits not on an item (a function) but on an expression block
inside `main`; the attribute itself can apply to functions, to structs, and to a single
expression block alike. In a real project this means that only a small part of a function —
say, a logging call or an extra check — can be placed behind a flag while most of the
function stays shared; tying the entire function to the flag is not required. The measured
class does not change: whatever level it is applied at, a block that stays off still never
enters compilation at all.

In the run above, `#[cfg(feature = "unopened")]` and `#[cfg(not(feature = "unopened"))]`
were written **at the same spot**, as two mutually exclusive blocks. This is another common
form flags take: one behavior runs while a flag is on, its opposite runs while the flag is
off, and both stay visible in the source. The compiler only compiles whichever of the two
matches the flag's state, and leaves the other out entirely, just like `never_checked` above
— the side that stays off is not faulty here, merely off; but the fact that the compiler
never looks at it does not change.

This measurement shows that a closed flag's body is not compiled *today*; it does not show
whether the body **would or would not compile** once the flag is turned on. The only way to
show that is to actually turn the flag on and try compiling, but `never_checked` and
`another_unchecked` are deliberately built so that they would not compile once turned on —
running a source that does not compile is not possible in this lesson. So the measurement
only proves this: **today, nobody checks a closed flag's body.**

Comparing this with an ordinary unused item makes the distinction clearer. If a function is
written tied to no flag at all but is never called, the compiler notices and prints an
unused warning — because that function did enter the compilation process, had its name
resolved and its type checked, and was never called. A closed flag's body never gets
this warning, because it never entered the compilation process at all; the reason for the
silence is not "known to be unused and ignored," it is "treated as if it had never existed."
Because every code block delivered in this lesson compiles without warnings, this comparison
is not run here, but the difference between the warning the compiler gives unused code and
the warning it does (not) give a closed flag's body explains why the nobody-checks-it class
belongs to closed flags specifically.

What happens once it is turned on is not known until it is turned on — and this unknown is
the direct reason a closed flag's body loses sync (rots) with the rest of the source over
time. As the source tree grows, as the types and function signatures around it change, a
closed flag's body never has to keep up with any of these changes; because the compiler
never checks it, the mismatch only surfaces on the day the flag is turned on.

This limit does not end with a single flag, either. Even if a source tree carries only two
independent flags, the on/off pair produces four possible compilation states; as the number
of flags grows, the number of possible states grows exponentially. An ordinary build
pipeline actually compiles only a few of these states — usually the extremes, where
everything is off or everything is on — and most of the combinations in between are never
tried. The run above showed one of these extremes by leaving both flags off in a single
source file; a state where `unopened` and `untried` are turned on **together** was never
built in this lesson, because such a state would bring both bodies into compilation, and
both are deliberately built so that neither would compile. As the number of untried
combinations grows, predicting when a closed flag will be turned on and what happens at that
moment also grows harder. This lesson showed only two flags, and only the most extreme of
the four possible combinations (both off); the remaining three combinations — only
`unopened` on, only `untried` on, both on — stayed outside this measurement's scope, and
that very exclusion is itself another instance of the measured class (nobody-checks-it).

#### Summary

- An item marked with `#[cfg(feature = "name")]` never enters the compiler while that flag
  is off; compilation finishes without warnings even if the item carries an undefined call
  and a type error.
- This observation was repeated with two different flags (`unopened`, `untried`) and two
  different kinds of error; the outcome stayed the same in both.
- The guarantee's class is **nobody checks it**: neither the compiler, nor a tool, nor the
  running program checks a closed flag's body.
- An ordinary runtime branch (like `if`) only removes the body from the execution path, and
  the body still passes type checking; `#[cfg(...)]` removes the body from the source tree,
  and type checking never even starts — the two forms of exclusion are not the same thing.
- This measurement's limit: it shows that a closed flag's body is not compiled today, but it
  does not show whether it would compile once turned on — that is known only once the flag
  is genuinely turned on.
- A body behind a flag that stays off cannot keep up with changes in the source over time;
  noticing this is left to the moment the flag is turned on.

#### Next Step

This lesson showed how a body nobody looks at can sit silently; the reason for that silence
was not a hidden error, it was that the body never entered the compilation process at all.
The next lesson asks the opposite question: a body enters compilation, passes type checking,
and can still run wrong — why is the compiler's checking not enough to say a body **runs
correctly**, and how much can a test that runs to close this gap be trusted? The Testing and
Property-Based Testing lesson answers this question, one the compiler never asks, not with a
closed flag this time but with a property tested by running.

### Testing and Property-Based Testing

Source: https://academia.sh/en/courses/rust-advanced/testing-and-property-based-testing

Counting whether a property holds across two hundred inputs produced by a fixed-seed, deterministic generator, and showing within what limit this count answers a question the compiler never asks.

The Dependencies and Feature Flags lesson measured a body the compiler never sees at all —
there, the guarantee never even started. But the previous lesson's closing question pointed
in a different direction: a body like `running_body` enters compilation, passes type
checking, has its names resolved correctly, and can still produce a **wrong** result. The
compiler checks that a body exists and that its type is consistent; it never checks that the
body is *correct* — correctness is a property of behavior, not of type, and type checking
knows nothing about behavior.

One way to close this gap is to test by running: run a body against specific inputs and
check whether the result carries an expected property. This is different from writing an
example test with a single fixed input — an example test only shows that something is
correct for that one input, and says nothing about any input that was not chosen. This
lesson does this not with a single fixed input but with a large number of inputs produced by
a fixed-seed generator — that is property-based testing's core idea: instead of a single
example, test the same property across many generated examples. That the inputs come from a
deterministic generator is not a side condition, it is one of the necessary requirements for
the observation this lesson establishes: if a generator relying on the system clock or the
operating system's source of randomness were used, the same test would run with different
inputs on every machine and every run, and the number of inputs it held for would change
from run to run — this lesson rules out that variability from the start. The test itself is
not without checking: what decides which property is tested, which range the inputs come
from, and how many inputs are enough is not the compiler, it is **the party who writes the
test**. The course's shared question — where does the compiler's guarantee end here — finds
its answer in this lesson: where type checking ends, behavior checking begins, but the party
who writes it also draws this checking's scope.

#### Testing a Property by Running

A property-based test is built from three parts: a source that produces random inputs, a
condition to test on each of those inputs, and a loop that counts how many inputs the
condition holds for. Rust's standard library has no random-number generator, and this
curriculum forbids third-party crates, so the generator is hand-written. **PL5.** The only
thing that needs to be random is the *distribution* of inputs — the test itself must stay
deterministic, or the same run would give a different result on different machines. The
generator below is a linear congruential generator that starts from a fixed seed and
produces the same sequence on every call: given the same seed, the output sequence is
bit-for-bit identical on every run.

The generator's state is a single `u64` number, updated on every call by multiplying and
adding two fixed numbers; `wrapping_mul` and `wrapping_add` silently wrap any overflow in
this operation, so it never halts with a panic. `length` narrows this raw number to an upper
bound (deciding how many elements an array carries), and `element` produces an integer
between `-500` and `499`. `vector` combines the two: it first draws a length, then produces
that many elements. The constants used come from a known family of linear congruential
generators, and the generator's *good distribution* is not this lesson's subject — the only
property sought here is that it produces the same sequence on every run with the same seed;
this property gave bit-for-bit identical numbers across every run above.

```rust
// linear_generator.rs — fixed-seed, deterministic pseudo-random generator
pub struct LinearGenerator {
    state: u64,
}

impl LinearGenerator {
    pub fn new(seed: u64) -> Self {
        LinearGenerator { state: seed }
    }

    pub fn next(&mut self) -> u64 {
        self.state = self
            .state
            .wrapping_mul(6364136223846793005)
            .wrapping_add(1442695040888963407);
        self.state
    }

    pub fn length(&mut self, upper_bound: usize) -> usize {
        (self.next() % (upper_bound as u64 + 1)) as usize
    }

    pub fn element(&mut self) -> i32 {
        (self.next() % 1000) as i32 - 500
    }

    pub fn vector(&mut self, max_len: usize) -> Vec<i32> {
        let n = self.length(max_len);
        (0..n).map(|_| self.element()).collect()
    }
}
```

The property to be tested is a classic example: reversing an array twice should return the
original array. This property is built using `Vec`'s `reverse` method and is tested against
every generated input. Two hundred inputs are produced, each carrying at most twenty
elements; the count of inputs the property holds for is tallied.

```rust
// property_test.rs — does applying reversal twice give identity
mod linear_generator;
use linear_generator::LinearGenerator;

fn reverse_reverse_identity(v: &[i32]) -> bool {
    let mut copy = v.to_vec();
    copy.reverse();
    copy.reverse();
    copy == v
}

fn main() {
    let mut generator = LinearGenerator::new(42);
    let total_inputs = 200;
    let mut held = 0;

    for _ in 0..total_inputs {
        let input = generator.vector(20);
        if reverse_reverse_identity(&input) {
            held += 1;
        }
    }

    println!("inputs tested: {}", total_inputs);
    println!("inputs the property held for: {}", held);
}
```

```
inputs tested: 200
inputs the property held for: 200
```

When the generator starts with seed `42`, the first array it produces carries fourteen
elements, the second eleven, the third eighteen — lengths, just like elements, change
deterministically with the fixed seed. Only the total and held counts are printed in this
run, but each of the two hundred arrays the generator produces arrives this way, with
different lengths and different elements; the upper bound given to `vector` (`20`) states
that an array can carry at most twenty elements, and at least zero (an empty array). The
smallest length was left at zero because an empty array is also a valid input — applying
reversal twice should also give identity on an empty array, and the generator does produce
this edge case from time to time.

Applying reversal twice is a small example chosen to make explaining property-based testing
easier; the same approach can be applied to larger bodies. Converting a structure a parser
produced back to a string and parsing it again should give the same result as the original
structure; a sorting function's output should carry the same elements as the input and have
its neighboring elements in order. The property chosen in this lesson is one of the
simplest — an operation combined with its own inverse gives identity — but the way of
measuring (generator, property, counter) stays the same: whatever body is tested, closing
the place the compiler does not check by running always requires the same three parts.

All two hundred inputs confirmed the property. **PL6.** This count is deterministic
together with the generator: as long as the seed is `42`, each of the two hundred arrays
produced arrives in the same order, the same length, and with the same elements; no matter
how many times the run is repeated, the result does not change. This also clarifies what the
word "random" means in this lesson: the produced inputs are *varied* (not written out one by
one by hand), but the production process is *not random* — it produces the same sequence on
every run.

**Distinguishing this from a related idea:** the Production Go: Testing, Tooling and
Performance course's Coverage Reporting lesson measured **which lines** a test ran — the
coverage ratio showed how much of the source passed through the run. This lesson does not
count lines; it counts **how many inputs** a property holds for. A line running does not
mean it runs correctly — it only means it ran; what this lesson measures is the strength of
the guarantee that running gives about correctness.

The guarantee's class here is **handed to the writer**: the compiler checked
`reverse_reverse_identity`'s type and stopped there; it never asked whether the function
gives the correct result *for every input*. The party who wrote the test was the one who
asked and answered this question — the writer decided which property to test, which range
the inputs would come from (`-500` to `499`, at most twenty elements), and how many inputs
(two hundred) would be enough. The compiler took no part in any of these decisions; it only
checked that the written test code itself was type-consistent.

The **handed-to-the-writer** class carries the same word as the transfer measured in the
`unsafe` Rust lesson, but it sits in a different place: there, what was handed off was the
contract that a memory access stays within bounds; here, what is handed off is the claim
that a behavior is *correct*. What the two share is that the compiler stops at some point
and leaves the remaining responsibility to whoever wrote the source. One consequence of this
transfer: if the `reverse_reverse_identity` function itself had been written wrong — say,
the second `reverse` call had been forgotten — the compiler would not have noticed that
either, because a wrongly written test passes type checking exactly like a correctly written
one. A test's correctness, just like the correctness of the body it tests, stays the
writer's responsibility.

How many inputs there are is also part of the same transfer. Two hundred was chosen in this
lesson; had fifty been chosen, the test would have seen fewer inputs, had two thousand been
chosen, more. As the number grows, the covered set of inputs widens, but no number covers
**all** inputs. The compiler expresses no opinion about this number at all — no matter how
small or how large the number is, the test code passes type checking and compiles the same
way. Deciding whether the number is sufficient is part of the same responsibility as
deciding the generator's range: both rest in the writer's hands, entirely outside the
compiler's sphere of checking.

#### A Bounding Measurement

The run above showed that the chosen property holds for **every** input — but that outcome
comes from the chosen property being a true mathematical fact: reversing an array twice
always gives back the original array, regardless of order. Not every property is like this.
The run below tests, with the same generator, a property that holds for some inputs but not
others: an array being equal to its own reverse, that is, a palindrome condition.

```rust
// bounding_test.rs — a property that holds for some inputs, not for others
mod linear_generator;
use linear_generator::LinearGenerator;

fn equals_own_reverse(v: &[i32]) -> bool {
    let mut reversed = v.to_vec();
    reversed.reverse();
    reversed == v
}

fn main() {
    let mut generator = LinearGenerator::new(7);
    let total_inputs = 200;
    let mut held = 0;
    let mut first_non_holding: Option<Vec<i32>> = None;

    for _ in 0..total_inputs {
        let input = generator.vector(6);
        if equals_own_reverse(&input) {
            held += 1;
        } else if first_non_holding.is_none() {
            first_non_holding = Some(input);
        }
    }

    println!("inputs tested: {}", total_inputs);
    println!("inputs the property held for: {}", held);
    println!("first non-holding input length: {}", first_non_holding.map(|v| v.len()).unwrap_or(0));
}
```

```
inputs tested: 200
inputs the property held for: 62
first non-holding input length: 3
```

**PL7.** The property held for sixty-two of the two hundred inputs, and failed to hold for
the remaining hundred thirty-eight — because the generator can also produce short arrays (at
most six elements, including zero), some inputs turned out palindromic by chance: an empty
array and a one-element array are always equal to their own reverse, and this raises the
holding count. The first non-holding input's length is three — a three-element array being
a palindrome depends on the elements themselves, not on its length.

This measurement limits what a passing property test means. In the previous run, the
property held for all two hundred of two hundred inputs, because the tested property was
*always* a true mathematical fact; in this run, the property held for only sixty-two inputs,
because the tested property is *sometimes* true. The test itself does not show the
difference between the two — both look like a "passing test," except that the count of
passing inputs is not entirely equal. A property test passing proves that the property holds
for the tested set of inputs; it says nothing about an input that was not tested. Two
hundred inputs may be a wide sample, but the generator draws from a bounded range (`-500` to
`499`, at most twenty elements) — an input outside this range, or beyond this length, was
never tested at all. **PL8.** The guarantee a passing test gives is bounded by the number
and range of inputs it tested; when a claim is written without being tied to that number,
the guarantee's real scope is hidden.

The measurement carries one more limit: the first non-holding input was reported only by its
length, not by its elements — this is not a choice made because of the environment, it is a
deliberate one. Real property-based testing tools, once they find a non-holding input,
reduce it to a smaller, more readable counterexample through a process called *shrinking*;
this lesson does not write such a shrinking step, it only reports the first non-holding
input's length. This is another limit of the measurement: showing that a property does not
hold is not the same as finding the most *understandable* counterexample — what is counted
in this lesson is only how many inputs it holds for, not which input is the most
illustrative counterexample.

The number sixty-two itself also carries a limit: it is the count these particular two
hundred inputs, produced with seed `7`, gave. Had seed `11` or `100` been chosen, the count
of holding inputs would have come out different even with the same property tested across
the same range — because the set of produced inputs changes depending on the seed. This
lesson does not measure that variability; with a single seed, it makes a single run
deterministic and reports that run's result. Determinism here guarantees repeatability (the
same seed always gives the same sixty-two), but it does not guarantee the tested range's
*representativeness* — that strength depends on how the range and the generator are
designed, not on a single run's result.

#### Summary

- Rust's standard library has no random-number generator; this lesson used a fixed-seed,
  hand-written linear congruential generator — the same seed produces the same input
  sequence on every run.
- The property that applying reversal twice gives identity held for all two hundred of the
  two hundred generated inputs.
- The guarantee's class is **handed to the writer**: the compiler checked the test code's
  type and stopped; the writer decided which property to test, over which input range, and
  with how many inputs.
- A property that is sometimes true (being equal to its own reverse), tested with the same
  generator, held for only sixty-two of the two hundred inputs — a passing test does not
  mean correct for every input.
- This measurement's limit: the guarantee a property test gives extends only as far as the
  number and range of tested inputs; an input outside the range was never tested at all.
- The number of inputs, their range, and the tested property itself — including how much of
  each is enough — are decisions made by the party who writes the test, not the compiler;
  the same three parts (generator, property, counter) apply to bodies other than reversal,
  too.

#### Next Step

This lesson measured that we can show, *by running*, that a property runs correctly, but
that this demonstration stays bounded by a limited number of inputs and the writer's
decisions. The next lesson applies the same "guarantee established by running" idea to a
completely different question: which of two implementations does less work can also only be
learned by running, a question the compiler never asks, just like correctness. But this time
what is tested is not correctness, it is operation count; and instead of a number of inputs,
this time it is how many comparisons and how many copies happen on a single input that gets
counted. The Benchmarking and Profiling lesson examines what this number *does* and does not
say — specifically, why not a duration.

### Benchmarking and Profiling

Source: https://academia.sh/en/courses/rust-advanced/benchmarking-and-profiling

Counting the number of comparisons and copies two separate sorting implementations make on the same array, and showing why this count is not enough to declare one implementation's duration shorter than the other's.

The previous lesson showed, by running, that a body runs *correctly*, but measured that
this demonstration stays bounded by a limited number of inputs and the decisions of whoever
writes the test. This lesson applies the same "guarantee established by running" idea to a
completely different question: which of two implementations does less work? The compiler
never asks this question, either — both implementations pass type checking, both compile,
and the compiler expresses no opinion at all about how many steps either takes to reach a
result.

This measurement carries a hard constraint specific to Rust: the compiler's own distribution
has a benchmarking attribute, but this attribute is not available on the stable release — it
can only be used on the compiler's unstable (nightly) branch. Because this lesson runs on
the stable compiler, it never uses this attribute; duration is never printed in any lesson of
this curriculum anyway — the value a duration takes on this machine does not hold on another
machine; the same program, even on the same machine, is affected by another process running
in the background, by what the cache happens to hold at that moment, or by the operating
system's scheduling decision. So the question "which implementation's duration is shorter"
is never asked in this lesson; what is asked instead is "which implementation does how many
operations." The Production Go: Testing, Tooling and Performance course measured performance
claims in Go with allocations-per-operation instead of duration; this lesson applies the
same principle to comparison and copy counts — both come from the same reasoning: a number
independent of the environment is a more reliable unit of measurement than a duration
dependent on it.

#### Comparing by Operation Count

Two sorting implementations that do the same job — selection sort and insertion sort — are
run on the same array. Both sort the result correctly, but each sorts by making a different
number of comparisons and a different number of copies. To make this difference visible,
both implementations write an increment to a shared counter on every comparison and every
copy.

Selection sort searches the array's remaining unsorted portion for the smallest element, and
once it finds it, moves it right behind the sorted portion, repeating this step to the end
of the array — at every step, all remaining elements are examined, but a swap happens only
once. Insertion sort instead starts from the beginning of the array and places each new
element by shifting it back into the correct position within the already-sorted preceding
portion — placing one element may require several shifts, but each shift only compares
against neighboring elements. Both implementations arrive at the same result; what is
measured is the number of steps they take to get there.

```rust
// operation_counter.rs — comparison and copy counter, shared between two sorting implementations
#[derive(Default)]
pub struct OperationCounter {
    pub comparisons: u64,
    pub copies: u64,
}

pub fn selection_sort(array: &mut [i32], counter: &mut OperationCounter) {
    let n = array.len();
    for i in 0..n {
        let mut smallest = i;
        for j in (i + 1)..n {
            counter.comparisons += 1;
            if array[j] < array[smallest] {
                smallest = j;
            }
        }
        if smallest != i {
            array.swap(i, smallest);
            counter.copies += 1;
        }
    }
}

pub fn insertion_sort(array: &mut [i32], counter: &mut OperationCounter) {
    let n = array.len();
    for i in 1..n {
        let value = array[i];
        let mut j = i;
        while j > 0 {
            counter.comparisons += 1;
            if array[j - 1] <= value {
                break;
            }
            array[j] = array[j - 1];
            counter.copies += 1;
            j -= 1;
        }
        array[j] = value;
        counter.copies += 1;
    }
}
```

**PL9.** `selection_sort` searches for the smallest remaining element at every step and,
once it finds it, places it with a single swap — so its comparison count is high, but its
copy count is low. `insertion_sort`, instead, places each element by shifting it into the
correct position within the already-sorted portion — its comparison count depends on the
input's arrangement, its copy count depends on how many elements get shifted. Both
implementations produce the same result, but by a different path.

```rust
// measurement.rs — operation count of two sorting implementations on the same array
mod operation_counter;
use operation_counter::{insertion_sort, selection_sort, OperationCounter};

fn main() {
    let input: [i32; 12] = [9, 3, 7, 1, 8, 2, 6, 4, 10, 5, 12, 11];

    let mut array_a = input;
    let mut counter_a = OperationCounter::default();
    selection_sort(&mut array_a, &mut counter_a);

    let mut array_b = input;
    let mut counter_b = OperationCounter::default();
    insertion_sort(&mut array_b, &mut counter_b);

    println!("selection result: {:?}", array_a);
    println!("insertion result: {:?}", array_b);
    println!("both gave the same result: {}", array_a == array_b);
    println!();
    println!("{:<16}{:<14}{}", "implementation", "comparisons", "copies");
    println!("{:<16}{:<14}{}", "selection", counter_a.comparisons, counter_a.copies);
    println!("{:<16}{:<14}{}", "insertion", counter_b.comparisons, counter_b.copies);
}
```

```
selection result: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
insertion result: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
both gave the same result: true

implementation  comparisons   copies
selection       66            9
insertion       32            34
```

On this twelve-element array, selection sort made sixty-six comparisons and nine copies;
insertion sort made thirty-two comparisons and thirty-four copies. **PL10.** Neither
implementation is "fewer" in both columns: selection sort loses on comparisons but wins on
copies, insertion sort is exactly the opposite. Producing a single number ("total
operations") would have hidden this difference; keeping two separate counters makes
visible, separately, which implementation uses more of which resource.

How merging comparisons and copies into a single total could mislead can be seen in these
numbers: once seventy-five (`66 + 9`) is added up against sixty-six (`32 + 34`), insertion
sort looks like it "did fewer operations." But this total counts a comparison and a copy as
the same unit — when in fact the two are different operations that can carry different
costs. The counter deliberately kept these two operations separate; a merged total hides how
economical selection sort is with copies (only nine).

These two counters are also a form of profiling. While benchmarking compares two
implementations against each other, profiling asks **where** a single implementation spends
its work — which step, which kind of operation makes up how much of the total work.
`OperationCounter`'s two separate fields do exactly this: almost all of selection sort's
work sits in comparisons (sixty-six comparisons against nine copies), while insertion
sort's work is distributed more evenly between the two kinds (thirty-two comparisons
against thirty-four copies). In a real program, a profiling tool usually shows which
function consumes how much of the total duration; this lesson uses operation kind instead of
duration, but the question is the same: where does the work go?

**Distinguishing this from a related idea:** the Production Go: Testing, Tooling and
Performance course's Benchmarking lesson measured how many allocations a body made per
operation — what was counted there was whether new memory was allocated on the heap, and how
many times that repeated within a function call. This lesson has no memory allocation at
all; both sorts sort the input array in place (`&mut [i32]`), building no new array or
structure. What is counted are the algorithm's direct steps, like comparisons and copies.
Both use a number instead of a duration, but what the number represents differs: one counts
resource consumption (how many times space was requested from the heap), this lesson counts
the algorithm's own work steps (how many times two values were compared, how many times a
value was moved).

The guarantee this number gives comes **from running**, not from the compiler — and nobody
watches this number continuously. The compiler checked both implementations' types,
confirmed both carry the same signature, but never asked which one makes fewer comparisons;
only the run above answered that question, and the answer is valid only for that run — if
the array changes, if either one's source code changes, there is no way to say the numbers
still hold without rerunning the same measurement. The class measured in this lesson is
**nobody checks it**: the two implementations' relative cost is not continuously watched by
any tool, it can only be learned with a one-off run whenever requested.

This is a separate place from the **handed-to-the-writer** class measured in the previous
lesson. There, a property test existed, and that test answered the same question again
whenever it was rerun — the responsibility had been handed to the writer, but it could be
made continuous in the form of a test. In this lesson there is no such test; the run above
is a one-off observation, and turning it into a test that reruns automatically whenever the
source changes lies outside this lesson's scope. Even if such a test had been written, the
writer would still be the one deciding what counts as "good" (which number needs to stay
below which limit) — but checking would at least become continuous then. As things stand
today, checking is limited to just this lesson's run.

#### A Bounding Measurement

The numbers above belong to a single input, a shuffled twelve-element array. How do the
numbers change when the same two implementations run on a different input? The run below
applies the same two functions to an already-sorted array.

```rust
// bounding_measurement.rs — same implementations, how operation count changes on an ordered array
mod operation_counter;
use operation_counter::{insertion_sort, selection_sort, OperationCounter};

fn main() {
    let ordered: Vec<i32> = (1..=12).collect();

    let mut a = ordered.clone();
    let mut counter_a = OperationCounter::default();
    selection_sort(&mut a, &mut counter_a);

    let mut b = ordered.clone();
    let mut counter_b = OperationCounter::default();
    insertion_sort(&mut b, &mut counter_b);

    println!(
        "selection comparisons={} copies={}",
        counter_a.comparisons, counter_a.copies
    );
    println!(
        "insertion comparisons={} copies={}",
        counter_b.comparisons, counter_b.copies
    );
}
```

```
selection comparisons=66 copies=0
insertion comparisons=11 copies=11
```

**PL11.** Selection sort's comparison count did not change: still sixty-six — even though
the input is already ordered, selection sort still scans every remaining element one by one,
because the only way to find the smallest is to look at all of them. Insertion sort's
comparison count, however, dropped from thirty-two to eleven, and its copy count dropped
from thirty-four to eleven: in an already-ordered array, every element is larger than the
one before it, so it keeps its place with a single comparison, and no shifting is needed at
all.

An input in the opposite direction gives an exactly opposite result. Had the array been
sorted from largest to smallest, insertion sort would have had to shift every element all
the way to the start of the sorted portion — its copy count would climb even above the
shuffled array's thirty-four, while selection sort's would again stay fixed at sixty-six.
The same two implementations, across three different input arrangements (shuffled, ordered,
reverse-ordered), stand in three different positions relative to each other: sometimes one
does less work, sometimes the other, sometimes both do the same fixed amount. Selection
sort's comparison count staying the same every time is not a flaw, it is the direct result
of the algorithm's structure — wherever an element sits, every remaining element still gets
scanned.

This almost reverses the first run's result, though not entirely: selection sort's
comparison count is still larger than insertion sort's, but the ratio between them changed
considerably. In the first array, insertion sort made fewer comparisons; the same holds in
this array, but the gap grew much wider — from thirty-two against sixty-six, to eleven
against sixty-six. The same two implementations, on a different input of the same length,
stand at a very different ratio relative to each other. **PL12.** This shows that operation
count is **dependent on the input**, and that a measurement taken on a single input says
nothing about another input — selection sort's comparison count is independent of the
input, while insertion sort's is entirely dependent on it.

Operation count itself does not mean duration, either. A comparison and a copy may not
carry the same cost — if the copied value is a large structure, a copy can be far more
expensive than an integer comparison; here, since an `i32` is copied, this difference is
small, but the counter knows nothing about this difference at all, it only counts how many
times each ran. It is also possible for an implementation that does fewer operations to
actually have a longer duration: the kind of operations, the memory access pattern, and the
hardware itself all affect the outcome.

Memory access pattern is a concrete example of this. While scanning the remaining elements,
selection sort reads through the array from start to end continually; insertion sort,
instead, only concerns itself with nearby neighbors. These two access patterns can use the
hardware's cache differently, and this difference never shows up in the operation count at
all — even if two implementations make the same number of comparisons, one may access
memory in an order more cache-friendly than the other's more scattered order. This lesson
does not measure such a difference, because measuring it would bring in real hardware and
real duration; here, only the question "how many times" is answered, not "in what
duration." So the table above should not be read as "insertion sort is better" — it should
only be read as "on this input, insertion sort made fewer comparisons and fewer copies," and
even that is no guarantee it will repeat on the next input.

#### Summary

- Two sorting implementations (`selection_sort`, `insertion_sort`) were compared by
  separately counting comparisons and copies with a shared counter; duration was never
  measured, printed, or compared anywhere.
- On a shuffled twelve-element array, selection sort made sixty-six comparisons and nine
  copies, insertion sort made thirty-two comparisons and thirty-four copies.
- The guarantee's class is **nobody checks it**: how many operations each implementation
  makes is not continuously watched by any tool, it can only be learned with a one-off run
  whenever requested.
- On an already-ordered array, selection sort's comparison count did not change (sixty-six),
  insertion sort's dropped to eleven — operation count depends on the input.
- Operation count does not mean duration: a comparison and a copy do not carry the same
  cost, and an implementation that does fewer operations can actually have a longer
  duration.
- A measurement taken on a single input says nothing about another input; a "fewer
  operations" claim cannot be written without being tied to which input it was measured on.

#### Next Step

This lesson showed that the relative cost of two implementations can only be learned by
running, that this learning stays bounded to a single input, and that the compiler expresses
no opinion on this at all — all three lessons (the closed flag in Dependencies and Feature
Flags, the guarantee established by running in Testing and Property-Based Testing, the
operation count in this lesson) are different faces of the same underlying fact: where the
compiler stops, the remaining responsibility either falls to nobody, or is handed to a
running process. The next lesson — this topic's final one — applies the same "where the
compiler never looks" question to an entirely different source: the explanation written next
to a code block. The Documentation lesson shows that no tool checks whether a documentation
sentence is correct, but that a runnable example embedded inside that same documentation can
genuinely be tested — that two different guarantee classes can stand side by side in the
same source.

### Documentation

Source: https://academia.sh/en/courses/rust-advanced/documentation

Measuring how two guarantee classes can stand side by side in the same source, by showing that a documentation sentence contradicts a function's real behavior without being checked by any tool, while that same function's runnable example inside the documentation is genuinely tested by invoking the documentation tool from within a program.

The previous three lessons measured three separate places that sit in the source but that
the compiler never looks at: a closed flag's body, a property tested by running, two
implementations' operation count. In all three, what was measured was a part of the source
that was *code* — a function, a block, a loop. This is the fourth and final lesson of this
topic, Project Lifecycle, and it asks the same question of the part of the source most often
overlooked: the explanation written next to a function. A documentation sentence is also
part of the source, but it is not syntax the compiler checks, it is prose a human reads;
this lesson asks who (or whether no one) checks this prose's correctness — and shows that
two different answers can stand side by side in the same source.

This question's answer cannot be given in a single sentence, because a documentation
comment has two different sections: the prose read above, and a small code example, usually
placed below it, showing how to call the function. These two sections sit together inside
the same comment, but their fates are not the same — that is exactly what this lesson
measures. This topic's other three lessons each built their own measurement; this lesson
returns to the course's twelve-observation guarantee core and uses its
documentation-accuracy observation directly.

#### The Documentation Itself, and the Contradiction in the Source

In Rust, writing lines starting with three slashes (`///`) above a function forms that
function's documentation comment. This comment differs from an ordinary explanation:
subheadings like `# Examples` and code fragments wrapped in backticks can be placed inside
it. All three functions below carry this same layout — an explanatory sentence, and an
example below it. The first function is the guarantee core's (`guarantee.rs`)
documentation-accuracy section: the documentation line says the function returns the
**largest** value, while the function actually returns the **smallest**. The other two
functions — one returning the smaller value, one summing a slice — have correctly written
documentation, and were added here for comparison.

```rust
// doc_body.rs — guarantee.rs's documentation-accuracy section + two more tested examples
/// This documentation line says the function returns the **largest** value.
/// The function returns the smallest. No tool checks this contradiction.
///
/// # Examples
///
/// ```
/// assert_eq!(doc_body::wrong_doc(3, 9), 3);
/// ```
pub fn wrong_doc(a: i32, b: i32) -> i32 {
    if a < b {
        a
    } else {
        b
    }
}

/// Returns the smaller of two values.
///
/// # Examples
///
/// ```
/// assert_eq!(doc_body::smaller_of(3, 9), 3);
/// ```
pub fn smaller_of(a: i32, b: i32) -> i32 {
    if a < b {
        a
    } else {
        b
    }
}

/// Returns the sum of a slice.
///
/// # Examples
///
/// ```
/// assert_eq!(doc_body::sum_slice(&[1, 2, 3]), 6);
/// ```
pub fn sum_slice(slice: &[i32]) -> i32 {
    slice.iter().sum()
}
```

**PL13.** This file carries no `fn main` at all; it stands as a source section. All three
functions compile correctly — even though `wrong_doc`'s prose is wrong, its body is
type-consistent, and the compiler never reads the prose at all. The body below calls these
three functions.

```rust
// doc_body_main.rs — the documentation contradiction stays in the source, the program still compiles
mod doc_body;
use doc_body::{wrong_doc, smaller_of, sum_slice};

fn main() {
    println!("doc says 'largest', real result: {}", wrong_doc(3, 9));
    println!("smaller_of(3, 9) = {}", smaller_of(3, 9));
    println!("sum_slice(&[1, 2, 3]) = {}", sum_slice(&[1, 2, 3]));
}
```

```
doc says 'largest', real result: 3
smaller_of(3, 9) = 3
sum_slice(&[1, 2, 3]) = 6
```

Compilation printed no warning at all. The `wrong_doc(3, 9)` call returned `3` — the smaller
value, not the larger one the documentation sentence claims. This is expected behavior in an
ordinary compilation process: `rustc` does not parse a documentation comment starting with
three slashes (`///`) as Markdown, does not read the sentences inside it, does not compile
the code example embedded within it. The entire documentation comment — both the wrong prose
and the correctly working example — is **invisible text** in an ordinary compilation; the
compiler neither confirms nor rejects it, because it never looks at it at all.

This is exactly the class the guarantee core measures: **nobody checks it**. The compiler
checked the function's *body* (checked its type, resolved its names) but never saw the
function's *documentation* at all. Even though a documentation sentence is part of the
source, it sits entirely outside the compiler's sphere of interest — just like the closed
flag's body in the Dependencies and Feature Flags lesson, but for a different reason: there,
the body never entered compilation at all; here, it is not that it fails to enter, but that
the compiler sees it as **belonging to another category entirely** — not code, prose.

It matters to keep these two situations apart. A closed flag's body is a piece of code that
could enter compilation once the flag is turned on — it is potentially checkable, merely
unchecked today. A documentation sentence's prose part, on the other hand, never enters
compilation under any condition; it was never code, and never will be. The compiler
separates a `///` documentation comment from the rest of the source and stores it as
information attached to the function — so it is not entirely ignored, it sits somewhere a
documentation tool can read it. But an ordinary compilation's type-checking and
name-resolution stages never look at this information's content at all: they do not verify
the sentence inside it, they do not compile the code example inside it. What is checked and
what is merely stored are different things.

This comes from the same root as the rot measured in the Dependencies and Feature Flags
lesson. There, a closed flag's body could lose sync as the rest of the source changed,
because nobody checked it. Here, the same thing happens between a function's body and its
documentation: the body can change over time — a bug gets fixed, a behavior gets reversed —
but the documentation sentence above it does not update itself. `wrong_doc` was probably
written correctly at some point (perhaps the function really did return the largest value),
and the documentation sentence was forgotten when the body changed; there is no way to prove
this, but the fact that both sides can live independently explains why such drift is
possible at all. What the two share is that no checking mechanism keeps the two sides tied
to each other.

#### The Example Inside the Documentation: Testing from Within a Program

Each function's documentation above has a `# Examples` section, containing a code example
wrapped in three backticks. Even though these examples are invisible in ordinary
compilation, the language's own documentation tool handles them specially: it scans the
source and compiles and runs each example as a separate test. This is the second form of
measurement built from within a program in this curriculum — in the Procedural Macros
lesson, the compiler itself was invoked from within a program; here, what is invoked is not
the compiler, but one of the compiler's side tools. The program below invokes this tool from
within itself, using `std::process::Command`, and prints only how many examples passed — no
path, duration, or machine name is printed.

```rust
// doc_test.rs — invokes the documentation tool from within the program and prints the passing test count
use std::process::Command;

fn main() {
    let build = Command::new("rustc")
        .args([
            "--edition", "2021", "--crate-type", "lib",
            "-o", "libdoc_body.rlib", "doc_body.rs",
        ])
        .output()
        .expect("could not start the compiler");
    if !build.status.success() {
        println!("passing tests: 0");
        return;
    }

    let test_run = Command::new("rustdoc")
        .args(["--edition", "2021", "--test", "-L", ".", "doc_body.rs"])
        .output()
        .expect("could not start the documentation tool");

    let output = String::from_utf8_lossy(&test_run.stdout);
    let count = output
        .lines()
        .find(|s| s.starts_with("test result:"))
        .and_then(|line| line.split("passed").next())
        .and_then(|part| part.trim().rsplit(' ').next())
        .and_then(|s| s.parse::<u32>().ok())
        .unwrap_or(0);

    println!("passing tests: {}", count);
}
```

```
passing tests: 3
```

**PL14.** All three examples passed — `wrong_doc`'s, `smaller_of`'s, and `sum_slice`'s. This
program runs in two steps. In the first step, it compiles the source as a library; this step
is necessary because every example calls the real functions in the source by their full
names, like `doc_body::wrong_doc`, and the documentation tool needs to find the pre-compiled
library to resolve these names. In the second step, the documentation tool is invoked in
test mode; the `-L .` option tells the tool to look for the pre-compiled library in the
working directory. The tool's own output contains, line by line, a path, a duration, and a
detailed breakdown; this program extracts only the single number from the "passed" line out
of that output and never prints the rest — no field dependent on the environment leaks into
this program's own output.

This run reverses the previous section's observation. In ordinary compilation, the
documentation was completely invisible; here, the documentation tool reads the same source
**with a different eye** — it treats the code fragment inside each `# Examples` block as a
separate source file and genuinely compiles and runs it. `wrong_doc`'s example tests real
behavior (the result `3`) with the equality-checking call inside it, and **passes** —
because the example code itself is written correctly, only the prose sentence above it is
wrong. The class measured in this lesson changes here: **still holds**. The documentation
tool is part of the language's own distribution, and, just like `rustc` checks a body, it
checks the example by compiling and running it.

This provides a defense against the rot described in the previous section — but only for the
example, not for the prose. Had `smaller_of`'s signature changed (say, if it took three
arguments instead of two), the call inside its example would no longer compile, and the
documentation tool would mark it "failed"; the example is **forced** to stay in sync with
the body. Nothing, however, keeps `wrong_doc`'s prose in sync with the body — whether the
body changes or not, the sentence stays exactly as it is. The guarantee the documentation
tool gives is therefore real but narrow: only the example is forced to stay in sync with the
body, not the explanation surrounding it.

**Distinguishing this from a related idea:** the Production Go: Testing, Tooling and
Performance course's measurement axis was **who** states a piece of knowledge — the source,
a tool, or a run. A documentation sentence is exactly a piece of knowledge **the source
states**; this lesson asks whether that knowledge is verified. The answer splits in two: the
prose part is read by no tool at all (nobody checks it), while the example part is run by
the language's own documentation tool (still holds) — the same source sentence's two
different parts meet two different fates. Compared to that course's three-way distinction, a
documentation sentence's prose falls exactly into the "stated by the source" category — no
tool or run verifies it; the documentation sentence's example, however, steps outside that
three-way split, because it stops being something the source merely states and becomes
something **a run verifies**.

#### A Bounding Measurement

Reading the two runs above together produces a striking result: `wrong_doc`'s example is
one of the three passing tests, and its **passing** does not change the fact that the prose
sentence above it is still wrong. The example tests `3`, and `3` is the correct result; the
prose says "largest," and that is wrong. Because the documentation tool runs the example and
gets the correct result, it marks the test as **passed** — without ever looking at what the
prose says.

**PL15.** This is this measurement's most important limit: what the documentation tool
checks is **the example, not the explanation.** An example tests the claims inside it (the
expected values written with the equality-checking call); the prose sentence above the
example neither influences nor checks what it says, in any way. A person could read
`wrong_doc`'s documentation, understand "returns the largest," see the example below the
same documentation, and even know that it passes — and still walk away from the source with
a wrong impression, because a passing example says nothing at all about the prose's
correctness.

This limit's scope is also clear-cut: the documentation tool sees only code blocks marked
with ` ``` `; every sentence outside these blocks, including the `# Examples` heading, is
ordinary text to it. An example being written correctly does not mean that example is
**consistent** with the explanation — it only means the example is correct on its own terms
(that it matches the real behavior in the source code). In this lesson, the `smaller_of`
function has exactly the same body as `wrong_doc`, but its prose is correct; both of their
examples pass the same kind of test, because the example only tests what gets called and
what gets returned at the code level, it does not compare the two functions' prose.

This limit has one more consequence: an example passing does not mean the case that example
**represents** is complete, either. `wrong_doc`'s example tests only a single call (`3` with
`9`); what the function returns when called with a different input never appears in this
example at all. While the documentation tool marks this example as passed, it says nothing
about what the function does for *every* input — exactly the same limit seen in the Testing
and Property-Based Testing lesson applies here too: exactly as much guarantee as the tested
input, no more, no less. The guarantee the documentation tool gives is therefore doubly
bounded: it tests only the example (not the prose), and it tests only the input the example
tests (not the function's entire behavior).

#### Summary

- In an ordinary compilation (`rustc`), both a documentation comment's prose and the code
  example inside it are invisible; the compiler looks at neither.
- `wrong_doc`'s documentation sentence says "largest," the body returns the "smallest"; this
  contradiction passes without warning in ordinary compilation — the class is **nobody
  checks it**.
- The documentation tool was invoked from within the program, and only the passing test
  count (`3`) was printed; duration, path, and machine name were never printed.
- The documentation tool genuinely compiles and runs the code inside `# Examples`; for this
  section, the class is **still holds** — the language's own tool carries `rustc`'s checking
  one step further.
- The bounding measurement: `wrong_doc`'s example passed, but the wrong prose sentence
  above it is still wrong — the documentation tool tests the example, not the explanation.
- The same function can carry two guarantee classes at once: its documentation's prose is
  nobody-checks-it, its documentation's example is still-holds.

#### Next Step

This topic's four lessons, Project Lifecycle, showed how the compiler's guarantee stops in
four separate places: in a closed flag's body (nobody checks it), in a test established by
running (handed to the writer), in a counted unit of operation (nobody checks it), and now
in a documentation sentence — where, for the first time, two classes (nobody-checks-it and
still-holds) appear together within a single lesson. All four are different views of the
same underlying fact: wherever the compiler stops, the remaining gap is either filled by
nobody, filled by the writer through running, or entered by one of the language's own tools
with a different kind of checking.

The course's final lesson — the course wrap-up — brings these four places together and turns
to look at a fifth, this one outside a source: a version string. That string, too, sits in
the source and corresponds to nothing; which version of a library preserves which signature
is the Publishing lesson's subject. This lesson leaves its mark there, too: a version
string, just like a documentation sentence, is a claim that sits in the source, that a human
reads, but that the compiler never checks.

### Publishing

Source: https://academia.sh/en/courses/rust-advanced/publishing

It is shown by running code that a version string sits in the source without ever being checked against the body, that version compatibility is modeled at the signature level, and that the same signature can produce different behavior — that the promise stops at the signature, not at the behavior.

The Documentation lesson showed that a passing documentation example does not mean the
explanation above it is correct: the documentation tool tests only the example, it never
reads the sentence above it at all. This final lesson carries the same distinction to two
other surfaces a package exposes to the outside — a version string and a signature. Both
look like a promise: the version string says "this package is in this state," the signature
says "this function takes this, returns that." Across the Rust Ecosystem and Advanced Topics
course, eleven lessons asked the same question in eleven different setups — where does the
compiler's guarantee stop here? — and the answer fell into one of four classes. This final
lesson applies those four classes to two promises and shows that the two do not fall into
the same class.

#### The Meaning of the Version String

The guarantee core run at the course's opening set aside one of its twelve observations for
this: a version string sitting in the source corresponded to nothing. The run below extends
that observation with a package manifest and a function that submits it to a package
registry — the registry's name is never written anywhere, the submission is only modeled.

```rust
// package_manifest.rs — the version string sits in the source, the function that models the submission never looks at it
struct PackageManifest {
    name: &'static str,
    version: &'static str,
}

const VERSION_STRING: &str = "0.1.0";

fn double(x: i32) -> i32 {
    x * 2
}

// The function that models the submission: takes the manifest, produces a confirmation string.
// It does not check the version field's content in any way - it accepts any string.
fn model_submission(manifest: &PackageManifest) -> String {
    format!("the {} package is marked as {} without changing the body", manifest.name, manifest.version)
}

fn main() {
    let manifest = PackageManifest { name: "measurement_crate", version: VERSION_STRING };
    println!("{}", model_submission(&manifest));
    println!("double(21) = {}", double(21));

    let second_manifest = PackageManifest { name: "measurement_crate", version: "0.2.0" };
    println!("{}", model_submission(&second_manifest));
    println!("double(21) = {}", double(21));
}
```

```
the measurement_crate package is marked as 0.1.0 without changing the body
double(21) = 42
the measurement_crate package is marked as 0.2.0 without changing the body
double(21) = 42
```

**PL16.** Both manifests point to the same `double` function, both produce the same call,
and both return `42` — but the `version` field changed from `"0.1.0"` to `"0.2.0"`.
`model_submission`'s signature never looks at what string the `version` field carries at
all: it accepts every value of type `&'static str`. While the body stays unchanged in the
source down to a single character, the version field can be moved up a digit by hand, and
the compiler looks for no consistency at all between the two.

This is the exact repetition of the observation from the course's opening guarantee core:
a version string sits in the source and nobody compares it against the body. The class is
**nobody checks it** — neither the compiler, nor a tool, nor a running program tests whether
the version field stays consistent with the body. Submitting to a package registry carries
the same limit: the registry accepts a submitted version string, but it never checks that
the string correctly reflects the change in the body — that check is left entirely to the
writer's judgment.

#### Version Compatibility and the Signature

**The distinguishing sentence:** the Production Go: Testing, Tooling and Performance course
measured module publishing and version compatibility along the axis of a piece of
knowledge's source — asking whether a version declaration came from the source, a tool, or
a run. What this lesson asks is different: not a version declaration, but where the
compiler's own checking stops when a signature is at stake.

The version string passed through no check at all, but a function's signature does not. The
run below compiles the same caller source against three separate library versions: the
first is the base version, the second is a next version written with the same signature but
a different body, the third is a version whose signature has changed. This is measured from
within the program — the sources are kept as strings, the compiler is invoked with
`std::process::Command`, only compiled/did-not-compile is printed, the error text is never
read.

```rust
// version_compatibility.rs — the same caller is tried against three library versions, only compiled/did-not-compile is printed
use std::env;
use std::fs;
use std::process::Command;

const CALLER: &str = r#"
fn main() {
    let result = convert(10);
    println!("result={}", result);
}
"#;

const VERSION_ONE: &str = "fn convert(x: i32) -> i32 { x + 1 }\n";
const VERSION_TWO_SAME_SIGNATURE: &str = "fn convert(x: i32) -> i32 { x * 2 }\n";
const VERSION_TWO_CHANGED_SIGNATURE: &str = "fn convert(x: i32, step: i32) -> i32 { x + step }\n";

fn compiles(source: &str, workdir: &std::path::Path, name: &str) -> bool {
    let path = workdir.join(format!("{}.rs", name));
    fs::write(&path, source).unwrap();
    let binary = workdir.join(name);
    Command::new("rustc")
        .args(["--edition", "2021"])
        .arg("-o").arg(&binary)
        .arg(&path)
        .output()
        .map(|c| c.status.success())
        .unwrap_or(false)
}

fn classify(success: bool) -> &'static str {
    if success { "compiled" } else { "did not compile" }
}

fn main() {
    let workdir = env::temp_dir().join(format!("version-compatibility-{}", std::process::id()));
    fs::create_dir_all(&workdir).unwrap();

    for (name, body) in [
        ("version_one", VERSION_ONE),
        ("version_two_same_signature", VERSION_TWO_SAME_SIGNATURE),
        ("version_two_changed_signature", VERSION_TWO_CHANGED_SIGNATURE),
    ] {
        let combined = format!("{}{}", body, CALLER);
        let success = compiles(&combined, &workdir, name);
        println!("{}: {}", name, classify(success));
    }

    let _ = fs::remove_dir_all(&workdir);
}
```

```
version_one: compiled
version_two_same_signature: compiled
version_two_changed_signature: did not compile
```

**PL17.** The same caller source — the `convert(10)` call and the line that prints the
result — stayed unchanged against all three versions. It compiled against the first and
second versions, because both carried the signature the caller expected; it did not compile
against the third version, because the third version had added a second parameter and the
caller passed only a single argument. The compiler genuinely tested a form of version
compatibility here — but what it tested was not the version number, it was the signature.

This stands in direct contrast to the version string falling into the nobody-checks-it
class. The class here is **still holds**: the compiler retests, at every compilation,
whether a library is still type-compatible with the caller, and this test is entirely
independent of the version number itself — the result would not change whether the second
version carried the same number as the first or a different one, because the compiler never
looks at the version field at all, only at the signature.

#### A Bounding Measurement

The compiler looking at the signature genuinely tests which version the caller keeps
compiling against — but this test never reaches into behavior at all. The run below calls
two versions carrying the exact same signature side by side.

```rust
// signature_and_behavior.rs — the same signature can give a different result; the promise stops at the signature, not at the behavior
fn convert_v1(x: i32) -> i32 {
    x + 1
}

fn convert_v2(x: i32) -> i32 {
    x * 2
}

fn caller<F: Fn(i32) -> i32>(convert: F, input: i32) -> i32 {
    convert(input)
}

fn main() {
    let input = 10;
    println!("v1: {}", caller(convert_v1, input));
    println!("v2: {}", caller(convert_v2, input));
}
```

```
v1: 11
v2: 20
```

**PL18.** `convert_v1` and `convert_v2` carry the exact same signature; `caller`'s generic
bound accepts both in the same way. But when called with the same input, they produce a
completely different result: one gives `11`, the other `20`. The measurement in the
previous section would never have caught this difference at all — what it tested there was
only the question "does the caller still compile," and this question would answer "yes" for
both versions.

This limit echoes the observation from the course's first lesson: there, a macro compiled
both when called with two semantically distant types, because it checked type fitness at
the syntax level, not at the level of meaning. Here, the same signature carries two
semantically distant bodies, and the compiler accepts both, because it checks type fitness
at the signature level, not at behavior. The promise — the guarantee that saying "version
compatible" gives — stops at the signature; it does not stop at behavior. When a new version
is submitted to a package registry, the compiler accepting that version only shows that the
signature stayed consistent, it does not show that the body does the same work as before.

#### Closing Table

| Lesson | What was measured | Class of guarantee | Bounding measurement |
|---|---|---|---|
| Declarative Macros | The body the macro produces passing type-checking, macro hygiene | still holds | The macro looks only at type fitness; the same macro called with `i32` and `bool` compiles in both |
| Procedural Macros | The generated output also being checked in a separate compilation step | still holds | A procedural macro must compile in its own crate — not a choice, a constraint of the language |
| Domain-Specific Languages | The macro's output also being checked again through an interface | still holds | Readable syntax does not bring correctness; a wrong route-handler binding compiles too, as long as the type fits |
| `unsafe` Rust | One operation staying unchecked while another in the same block stays checked | still holds · handed to the writer | A handed-off guarantee is invisible when used correctly; the difference lies in the responsibility for misuse |
| The Foreign Function Interface | Two C functions being called by trusting only their declared signature | handed to the writer · outside the language | Had the declaration been written wrong, the compiler could not have noticed |
| The WebAssembly Target | A decision that changes by target being separated out in the source | outside the language | Source looking portable does not mean it is portable |
| Embedded Targets | A `no_std` environment taking allocation and panic behavior into its own hands | outside the language | Allocation-free code is not always possible; a fixed buffer demands an upper bound |
| Dependencies and Feature Flags | A closed flag's body never entering compilation at all | nobody checks it | Shows it is not compiled today, does not show whether it would compile once the flag is turned on |
| Testing and Property-Based Testing | A property being tested with a fixed-seed generator | handed to the writer | A passing test is true only for the tested input set; anything outside the range is never tested |
| Benchmarking and Profiling | Two sorting implementations' comparison and copy counts | nobody checks it | Operation count does not mean duration; an implementation doing fewer operations can in fact take longer |
| Documentation | The documentation example being tested, the explanation above it not being tested | still holds · nobody checks it | The documentation tool tests the example, not the explanation; the same function can carry both classes at once |
| Publishing | The meaning of the version string and version compatibility being tested at the signature level | nobody checks it · still holds | The same signature can give different behavior — the promise stops at the signature, not at the behavior |

#### Summary

- The version string sits in the source and never passes any check against the body; the
  class is **nobody checks it** — even the model that submits the version declaration to a
  package registry never looks at the version field's content.
- The same caller source compiled against three library versions: it compiled against the
  two versions whose signature stayed the same, and did not compile against the version
  whose signature changed; the class is **still holds** — the compiler looks at the
  signature, not at the version number.
- Two versions carrying the same signature gave different results when called with the
  exact same input (`11` versus `20`); the signature-level check of version compatibility
  never reaches into behavior — the promise stops at the signature, not at the behavior.
- Four classes stayed unchanged throughout the course: still holds, handed to the writer,
  outside the language, nobody checks it; the twelve observations split 4/3/2/3, and this
  final lesson added one example each to the still-holds and nobody-checks-it ends of that
  split.
- The submission to a package registry was modeled here without carrying an actual network
  call, using only a manifest and a function that accepts it; the registry's name was never
  written anywhere.

#### Course Wrap-Up

The Rust Ecosystem and Advanced Topics course asked one question in twelve different setups
across twelve lessons: where does the compiler's guarantee stop here? The answer fell into
one of four classes, and these four names stayed unchanged throughout the course — still
holds, handed to the writer, outside the language, nobody checks it; the twelve observations
split four, three, two, three. The Macros and Metaprogramming topic showed that generated
source is checked like ordinary source, but that this check works only at the type level.
The Low-Level and Interoperability topic showed where checking is handed off — to the writer
or to another language — and that a handed-off guarantee stays invisible when used
correctly. The Project Lifecycle topic showed, one after another, that the source sometimes
has corners that are never checked at all — a closed flag's body, an operation count turned
into a duration, a documentation sentence's correctness, a version string's meaning. This
final lesson placed two of those corners side by side: a version string is never checked at
all, but that same version's signature is still checked.

The Rust curriculum closes with five courses, and each asked the same language a completely
different question. Rust Fundamentals asked whether a written line produces a value — the
expression, statement, item distinction. Ownership and the Memory Model asked where that
value's lifetime ends. Traits, Generics and Error Handling asked how many times a body runs.
Concurrency and Asynchronous Rust asked what a task needs in order to make progress. The
Rust Ecosystem and Advanced Topics course asked where the compiler's guarantee stops. The
five questions looked at five different layers of the same language: first the written line
itself, then a value's lifetime in the source, then how many times a body runs, then what a
task needs in order to make progress, and finally where the checking that provides all of
this itself stops.

The next curriculum moves on to C and C++; which measurement axis will be chosen for these
two languages is not yet settled in this lesson.

