---
title: 'Derived Traits'
source: 'https://academia.sh/en/courses/rust-abstraction/derived-traits'
course: 'Traits, Generics and Error Handling'
language: en
updated: '2026-09-15T04:58:42+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Shows that the derive attribute implements a trait without hand-writing it'
    - "Compares a hand-written body's run count being countable against a derived body's not being countable, and explains why"
    - 'Demonstrates by running it that the same derive can give different results on different data layouts'
---

# Derived Traits

An attribute line can implement a trait without hand-writing it — derive; a hand-written body can place a counter call into the run ledger, while a derived body has no source line to count it in, so it can only be observed by its behavior — and the same derive gives two different results on two different data layouts.

The previous four lessons measured hand-written bodies: a trait's default body, a bounded
generic body, a body carrying an associated type, a body called statically or dynamically —
all of them stood somewhere in the source, under an `fn` line. A trait implementation is
sometimes never hand-written at all: the `#[derive(...)]` attribute line tells the compiler "you
generate this trait for this type," and produces the body without it ever being written in the
source. This lesson asks how many times a body of this kind runs — and shows that this time the
question cannot be answered with an exact number.

## The Difference Between Hand-Written and Derived

An equality comparison for a type can be built two ways: by hand-writing the `impl PartialEq
for Type` block, or by placing the `#[derive(PartialEq)]` line above the type. Both produce the
same result — they say whether two values are equal field by field — but their counterpart in
the source is different.

```rust
// times.rs — slice: the record/times ledger, a hand-written equality body.

use std::cell::RefCell;
use std::collections::BTreeMap;
use std::rc::Rc;

pub type Ledger = Rc<RefCell<BTreeMap<String, u32>>>;

pub fn record(ledger: &Ledger, name: &str) {
    *ledger.borrow_mut().entry(name.to_string()).or_insert(0) += 1;
}

pub fn times(ledger: &Ledger, name: &str) -> u32 {
    *ledger.borrow().get(name).unwrap_or(&0)
}

// Hand-written implementation: the body sits in the source, it can be counted.
pub struct ManualEqual {
    pub x: i32,
    pub y: i32,
    pub ledger: Ledger,
}

impl PartialEq for ManualEqual {
    fn eq(&self, other: &Self) -> bool {
        record(&self.ledger, "manual-equality-body");
        self.x == other.x && self.y == other.y
    }
}

// Derived implementation: the body is not in the source, the compiler generates a field-by-field comparison.
#[derive(PartialEq)]
pub struct DerivedEqual {
    pub x: i32,
    pub y: i32,
}
```

`ManualEqual`'s `eq` method carries a two-line body in the source: first a record call, then a
field-by-field comparison. `DerivedEqual` carries no `impl PartialEq` block at all — its only
line is `#[derive(PartialEq)]`, and this line is not a method body, it is an **instruction** to
the compiler. When the compiler reads this instruction, it generates its own `eq` body; this
body does the same job as `ManualEqual`'s (comparing the `x` fields and the `y` fields), but it
never appears in any line of the source.

A single `#[derive(...)]` line can produce more than one trait at once; trait names separated
by commas are written inside the parentheses. `Clone`, already seen in this course, can be
derived this way too, and so can `Debug` (a trait that turns a value into a readable text
form):

```rust
// derive-debug.rs — a Debug derive turns fields into a text form automatically.
#[derive(Debug, Clone, PartialEq)]
struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let a = Point { x: 3, y: 5 };
    let b = a.clone();
    println!("{:?}", a);
    println!("is_equal={}", a == b);
}
```

```
Point { x: 3, y: 5 }
is_equal=true
```

All three traits (`Debug`, `Clone`, `PartialEq`) were derived on a single line, and none of the
three has a body visible in the source. The `{:?}` output shows that `Debug`'s generated body
gathers up the field names and values on its own — this pattern was not hand-written either, it
was generated by the compiler, just like `eq`. These three traits can be derived together
because all three rest on the same principle: apply the same operation (printing, cloning,
comparing) to every field of the type, in order. This same shared principle will come up again
in a later lesson (serialization).

## Countable and Uncountable

```rust
// derive-uncountable.rs — a hand-written body can be counted, a derived body cannot.
mod times;
use times::{times, Ledger, ManualEqual, DerivedEqual};
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::rc::Rc;

fn main() {
    let ledger: Ledger = Rc::new(RefCell::new(BTreeMap::new()));
    let a = ManualEqual { x: 1, y: 2, ledger: Rc::clone(&ledger) };
    let b = ManualEqual { x: 1, y: 2, ledger: Rc::clone(&ledger) };
    let c = ManualEqual { x: 9, y: 9, ledger: Rc::clone(&ledger) };

    let is_equal = a == b;
    let is_not_equal = a == c;
    println!("manual: is_equal={} is_not_equal={}", is_equal, is_not_equal);
    println!("manual-equality-body={}", times(&ledger, "manual-equality-body"));

    // The derived PartialEq's body is not written in the source - so we cannot put
    // a record call inside it. We can only observe its behavior.
    let p = DerivedEqual { x: 1, y: 2 };
    let q = DerivedEqual { x: 1, y: 2 };
    let r = DerivedEqual { x: 9, y: 9 };
    println!("derived: is_equal={} is_not_equal={}", p == q, p == r);
}
```

```
manual: is_equal=true is_not_equal=false
manual-equality-body=2
derived: is_equal=true is_not_equal=false
```

**TG10.** `manual-equality-body` ran twice — both the `a == b` and `a == c` calls triggered the
`eq` body, because a record call was hand-placed inside the body. This row falls into the
**every time** class: the `==` operator runs unavoidably every time it is used. We cannot write
the same row for `DerivedEqual` — since we cannot add a single source line inside the body the
compiler generates, we cannot place a counter to show how many times it ran. We can see that
both the `p == q` and `p == r` calls give the correct result (a behavioral observation), but we
cannot write the count of how many times the body behind those two calls was triggered into the
run ledger. This shows that a body with no line written in the source **cannot be placed with
certainty into any of the course's three classes** — that it runs is certain (a call always
produces a result), but its run count cannot be measured with this method.

The Object-Oriented Java course showed that a record type's equality and text representation
are built automatically — what was measured there was which type made the decision, not
whether the record's own generated body runs. This lesson looks at a similar automatic
generation, but asks a different question: can this generated body be counted by the course's
counting-based method? The answer is no — the lesson says so openly, it does not make up a
number.

A derived body being uncountable does not mean it is random or unpredictable — quite the
opposite, what `#[derive(PartialEq)]` will produce is fully deterministic: it compares the
type's fields one by one in declaration order, `true` if all are equal, `false` if even one
is not. This rule is the same for every `#[derive(PartialEq)]`, and can be stated in advance just
by looking at the type declaration, without ever running it. What cannot be counted is not the
behavior itself, it is that behavior's run-time **call count**: to see how many times `eq` runs,
we need to add a line to the run ledger, and there is no line to add it to in derived source.

"The body is not in the source" does not mean "no body compiles." The `#[derive(...)]` line
**generates**, at compile time, a piece of code resembling an `impl` block, and this generated
code compiles the same way a hand-written `impl` block would — the generation happens in a
separate stage, before the rest of compilation. In the source the author sees, this generated
code never appears at all — only the `#[derive(PartialEq)]` line is visible, the dozens of
lines of comparison logic it produces are not. What this lesson means by "an unwritten body
still runs" is exactly this: something runs, it genuinely executes, but it has no counterpart in
the source the author **reads**. This is also why we cannot add a counter call — adding a counter
needs a source line to add it onto, and there is no such line in derived source.

## Same Derive, Different Data Layout, Different Result

A derive having no body in the source might give the impression that it means the same thing
for every type. It does not: the same `#[derive(Clone)]` line produces two different behaviors on
two different data layouts. `derive` always says "apply the same operation field by field," but
what "the same operation" means depends on the field's own type — cloning an `i32` means writing
a new number, cloning an `Rc<...>` means incrementing a counter and sharing the same cell.

```rust
// derive-layout.rs — the same derive gives two different results on two data layouts.
use std::cell::RefCell;
use std::rc::Rc;

#[derive(Clone)]
struct Linked {
    value: Rc<RefCell<i32>>,
}

#[derive(Clone)]
struct Separate {
    value: i32,
}

fn main() {
    let a = Linked { value: Rc::new(RefCell::new(1)) };
    let b = a.clone();
    *a.value.borrow_mut() += 41;
    println!("does the linked clone share: {}", *b.value.borrow() == 42);

    let c = Separate { value: 1 };
    let mut d = c.clone();
    d.value += 41;
    println!("does the separate clone share: {}", c.value == d.value);
}
```

```
does the linked clone share: true
does the separate clone share: false
```

**TG11.** `Linked`'s clone **shares** the value in the source: a change made through `a` is also
seen in `b`, because the derived clone of an `Rc<RefCell<i32>>` field just increments the
counter and hands back a second reference to the same cell — the behavior built in the
Ownership and the Memory Model course's Reference Counting lesson. `Separate`'s clone, on the
other hand, is **independent**: a change made on `d` never touches `c` at all, because the
derived clone of an `i32` field produces an independent copy on its own. The
`#[derive(Clone)]` line is exactly identical on both, but the result diverges by data layout —
derive is a shortcut for saying "clone field by field," it does not concern itself with what the
field itself means.

This distinction shows once more the difference between a hand-written body and a derived one:
a hand-written `impl Clone` could, if it wanted, be written to produce an independent copy for
`Linked` too — by reading the value inside the `RefCell` and building a new `Rc`. A derived body
does not offer this choice; it always says "clone the fields as they are," inheriting whatever the
field's own `Clone` implementation does. Whether a type's clone will be shared or independent is
decided not by the derive but by the choice of field types.

Derive also has a quiet precondition: writing `#[derive(PartialEq)]` for a struct requires that
**every field** of the struct itself implements `PartialEq` — the compiler calls `==` for every
field while generating the field-by-field comparison, and this call needs to compile, which
requires the field's own type to be comparable. `Point`'s `x` and `y` fields being `i32`, and
`i32` already carrying `PartialEq`, is why the derive compiles without a problem. If one of the
fields carried an incomparable type (a function pointer, say), the same `#[derive(PartialEq)]`
line would be rejected by the compiler — this rejection, too, by the course's general rule, is not
put in a code block; the `Point` and `ManualEqual` examples above already show the accepted
counterpart: structs carrying only comparable fields.

## The Measurement's Limit

Derive does not mean the same thing on every type. The same `#[derive(Clone)]` can produce a
shared copy on one type and an independent copy on another; the same `#[derive(PartialEq)]` can
compare two fields on one type and ten fields on another — **what** the generated body does
depends on the fields of the type it is built on. This lesson does not count how many times a
derived body ran, and it does not assume that two different derives do the same job either; it
only shows that both are generated by the compiler and that their behavior can be observed by
running them. The course's measurement method hits a clear limit here: wherever a counter call
cannot be written in the source, **behavior comparison** takes over instead of a number.

This limit has a practical consequence: trusting a derived body requires understanding that
body's **design**, because there is no way to trace it at run time. Writing `#[derive(Clone)]`
for `Linked`'s `Rc<RefCell<i32>>` field makes the decision "let this type's clone be shared"
**invisibly** — this decision is not visible to someone reading the type declaration from the
`derive` line, it is visible from the field's own type. A hand-written `impl Clone` could show
this decision openly inside the body (is a reference being cloned, or a value); a derived body
hands this decision off to the field type and does not write it in the source separately. This is
the cost of derive's shortcut: the source gets shorter, but whoever reads the source has to go
down to the field types to understand the behavior.

The previous four lessons could measure the run count directly with a `record` call, because
the body always stood somewhere under an `fn` line in the source. This lesson breaks that
assumption: the body is not in the source, but the behavior is, and it can be verified. The
course's measurement method chooses to stay silent where it cannot count — instead of making up a
number, it says "there is no number here."

## Summary

- The derive attribute (`#[derive(...)]`) implements a trait without hand-writing it, by having
  the compiler generate it; it leaves no method body in the source.
- A hand-written body (`manual-equality-body`) falls into the every-time class and can be
  counted — it ran twice in this lesson; a derived body runs every time it is called too, but
  since it has no body in the source, this run cannot be counted.
- An uncountable body's behavior can still be observed by running it: whether it gives the
  correct result can be seen, how many times it ran cannot.
- The same derive can give two different results on two different data layouts — it produces a
  shared copy on a type with an `Rc<RefCell<...>>` field, an independent copy on a plain
  numeric field.

## Next Step

This lesson showed that a body runs even when it is **never written at all**. The course's last
lesson looks at another kind of invisibility: where an operator (like `+`) is called never
appears in the source, only the operator itself is visible. The next lesson counts how many
times this invisible call runs by running a three-term sum.
