# Impertio Studio Rust Claude Skill Package Rust Core Type System

> Rust Core: Type System

- Skill: `tomevault-io/impertio-studio-rust-claude-skill-package-rust-core-type-sys` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add tomevault-io/impertio-studio-rust-claude-skill-package-rust-core-type-sys`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tomevault-io/impertio-studio-rust-claude-skill-package-rust-core-type-sys/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: tomevault-io (https://skillmd.com/u/tomevault-io)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tomevault-io/impertio-studio-rust-claude-skill-package-rust-core-type-sys

---


# Rust Core: Type System

Conceptual map of how Rust types are identified, laid out in memory, optimized, and composed. Read this before reaching for `#[repr(C)]`, `PhantomData`, `NonZero*`, or the never type `!`.

For const-generic mechanics see [[rust-syntax-generics]]. For the edition-2024 never-type fallback semantics see [[rust-syntax-edition-2024]]. For repr(C) in FFI bindings see [[rust-impl-ffi-bindgen]].

## Quick reference

| Concept | One-liner | Use when |
|---------|-----------|----------|
| Nominal typing | Identity is by name, not shape | ALWAYS expect newtype `struct Meters(f64)` to be distinct from `struct Seconds(f64)` |
| ZST | `()`, `PhantomData<T>`, `struct Unit;` occupy 0 bytes | Marker types, type-state, encoding info at zero runtime cost |
| `#[repr(Rust)]` (default) | Compiler chooses layout, may reorder fields | ALL pure-Rust code (best memory packing, no FFI) |
| `#[repr(C)]` | C layout, declaration order, predictable padding | FFI boundary, struct shared with C/C++ |
| `#[repr(transparent)]` | Single non-ZST field, same ABI as that field | Newtype wrappers that must be ABI-identical to inner |
| `#[repr(packed(N))]` | Disable padding (DANGEROUS) | Network/binary protocols with explicit alignment rules; never take `&field` |
| `#[repr(align(N))]` | Force higher alignment | Cache-line padding, hardware alignment requirements |
| `#[repr(u8)]` on enum | Stable discriminant size | FFI enums, on-wire encoding |
| Niche optimization | Invalid bit patterns encode `None` | `Option<NonZeroU32>` = 4 bytes (not 8), `Option<&T>` = pointer size |
| Never type `!` | Empty type, coerces to any other type | Return type of `panic!`, `loop {}`, `return`, `continue` |
| Type-state pattern | `PhantomData<State>` encodes state at compile time | Builders requiring ordered configuration; resources with lifecycle |
| Const generics | Type parameters that are values, e.g. `<const N: usize>` | Fixed-size arrays, dimension-tagged matrices |

## Decision trees

### Which `repr` do I need?

```
Is this struct/enum crossing an FFI boundary?
├── No  : Use default repr (no annotation). Compiler optimizes layout.
└── Yes
    ├── Is it a newtype wrapping ONE non-ZST field with same ABI?
    │   └── Use #[repr(transparent)]
    ├── Is it a multi-field struct shared with C?
    │   └── Use #[repr(C)]
    ├── Is it a field-less enum with stable discriminant?
    │   └── Use #[repr(u8)] / #[repr(i32)] / etc.
    └── Are you implementing a wire/binary protocol with explicit byte layout?
        └── Use #[repr(C, packed(N))]. NEVER take references to fields.
```

### When do I reach for `PhantomData<T>`?

```
Does the struct's type signature have a generic parameter T it does NOT store?
├── No  : Don't add PhantomData.
└── Yes : You need to express the relationship to T anyway. Use PhantomData<T>.
    ├── Want covariance + drop check? -> PhantomData<T>
    ├── Want invariance? -> PhantomData<fn(T) -> T>
    ├── Want contravariance? -> PhantomData<fn(T) -> ()>
    └── Want !Send / !Sync opt-out? -> PhantomData<*mut ()>
```

### When does niche optimization apply?

```
Is the inner type guaranteed to have an invalid bit pattern?
├── NonZero* (NonZeroU8..NonZeroU128, NonZeroUsize, NonZeroI*) : YES, zero is the niche
├── &T, &mut T, Box<T>, fn pointers                            : YES, null is the niche
├── NonNull<T>                                                  : YES, null is the niche
├── char                                                        : YES, surrogate range + values >0x10FFFF are niches
├── bool                                                        : YES, only 0 and 1 are valid
└── Plain u32, i32, f32                                         : NO, every bit pattern is valid
```

If yes : `Option<T>` and `Result<T, ZST>` have the same size as `T`.
If no  : `Option<T>` is at least `T` + 1 byte (likely more due to alignment padding).

## Patterns

### Pattern : Nominal typing for unit safety

```rust
// Two structs, identical shape, DIFFERENT TYPES. Compiler enforces conversion.
struct Meters(f64);
struct Feet(f64);

fn distance_in_meters(d: Meters) -> Meters { d }
// distance_in_meters(Feet(10.0));  // ERROR : expected Meters, found Feet
```

ALWAYS prefer newtype wrappers over raw primitives at API boundaries. NEVER pass `f64` between modules when the value has units, IDs, or domain constraints.

### Pattern : ZST as marker

```rust
struct LoggingEnabled;
struct LoggingDisabled;

struct Logger<S> {
    target: String,
    _state: std::marker::PhantomData<S>,
}

impl Logger<LoggingDisabled> {
    fn enable(self) -> Logger<LoggingEnabled> {
        Logger { target: self.target, _state: std::marker::PhantomData }
    }
}

impl Logger<LoggingEnabled> {
    fn log(&self, msg: &str) { println!("[{}] {}", self.target, msg); }
}
```

`size_of::<PhantomData<T>>() == 0`. The state is enforced entirely at compile time, with zero runtime cost. Per [std::marker::PhantomData][phantom-doc] : `align_of::<PhantomData<T>>() == 1`.

### Pattern : `repr(transparent)` newtype for FFI

```rust
#[repr(transparent)]
pub struct FileDescriptor(i32);

// Same ABI as i32. Safe to pass to C functions expecting int.
// Same niche behaviour as i32 (none, so Option<FileDescriptor> is larger).
```

ALWAYS use `#[repr(transparent)]` for FFI newtypes that wrap a single primitive. NEVER use `#[repr(C)]` for single-field newtypes when you want to preserve the inner type's ABI (repr(C) gives the struct C-struct ABI, which differs from primitive ABI on some platforms).

### Pattern : Niche-optimized handles

```rust
use std::num::NonZeroU32;

pub struct EntityId(NonZeroU32);
// Option<EntityId> is 4 bytes, NOT 8 bytes.
// `None` is encoded as the bit pattern 0, which is invalid for NonZeroU32.

impl EntityId {
    pub fn new(raw: u32) -> Option<Self> {
        NonZeroU32::new(raw).map(EntityId)
    }
}
```

Per [std::num::NonZeroU32][nonzero-doc] : `size_of::<Option<NonZeroU32>>() == size_of::<u32>()`. ALWAYS prefer `NonZeroU32` (or `NonZeroUsize`) for IDs and handles where 0 is invalid. The compiler reclaims the zero bit pattern for the `None` discriminant.

### Pattern : Never type for diverging functions

```rust
fn fatal(msg: &str) -> ! {
    eprintln!("FATAL: {msg}");
    std::process::exit(1);
}

let x: i32 = match parse_input() {
    Ok(n) => n,
    Err(_) => fatal("bad input"),  // ! coerces to i32 (or anything)
};
```

The never type `!` is the type of expressions that never produce a value. It coerces to any other type, which is what makes `panic!`, `loop {}`, `return`, and `continue` usable in arbitrary positions.

### Pattern : Type-state builder

```rust
use std::marker::PhantomData;

pub struct NeedsName;
pub struct NeedsAge;
pub struct Ready;

pub struct UserBuilder<State> {
    name: Option<String>,
    age: Option<u32>,
    _state: PhantomData<State>,
}

impl UserBuilder<NeedsName> {
    pub fn new() -> Self {
        Self { name: None, age: None, _state: PhantomData }
    }
    pub fn name(self, name: String) -> UserBuilder<NeedsAge> {
        UserBuilder { name: Some(name), age: self.age, _state: PhantomData }
    }
}

impl UserBuilder<NeedsAge> {
    pub fn age(self, age: u32) -> UserBuilder<Ready> {
        UserBuilder { name: self.name, age: Some(age), _state: PhantomData }
    }
}

impl UserBuilder<Ready> {
    pub fn build(self) -> User { User { name: self.name.unwrap(), age: self.age.unwrap() } }
}

pub struct User { name: String, age: u32 }

// Compile-time enforcement : .build() unreachable until .name() and .age() called in order.
```

## Primitive types reference

| Type | Size | Niche | Notes |
|------|------|-------|-------|
| `i8`..`i128` | 1..16 bytes | No | Two's complement |
| `isize` | pointer-sized | No | Platform dependent |
| `u8`..`u128` | 1..16 bytes | No | Unsigned |
| `usize` | pointer-sized | No | Index type for slices/arrays |
| `f32`, `f64` | 4, 8 bytes | No | IEEE 754 |
| `bool` | 1 byte | Yes (only 0/1 valid) | `Option<bool>` = 1 byte |
| `char` | 4 bytes | Yes | Unicode scalar value, not UTF-8 code unit |
| `()` (unit) | 0 bytes | n/a | ZST, sole inhabitant `()` |
| `!` (never) | 0 bytes | n/a | No inhabitants, coerces to any type |

`char` is ALWAYS 4 bytes. NEVER conflate `char` with a UTF-8 byte (use `u8` for raw bytes or iterate `str::chars()` for scalars).

## Edition 2024 : never-type fallback change

CRITICAL behavioural change in edition 2024 (Rust 1.85+). Before edition 2024, type inference allowed `!` to fall back to `()`. In edition 2024, it falls back to `!` itself.

```rust
// Edition 2021 behaviour : T inferred as ()
// Edition 2024 behaviour : T inferred as ! -> compile error if ! does not satisfy bounds
fn outer<T>(x: T) -> Result<T, ()> {
    fn f<T: Default>() -> Result<T, ()> { Ok(T::default()) }
    f()?;       // Edition 2024 : T = !, but ! : Default not satisfied -> ERROR
    Ok(x)
}

// Fix : explicit turbofish
fn outer_fixed<T>(x: T) -> Result<T, ()> {
    fn f<T: Default>() -> Result<T, ()> { Ok(T::default()) }
    f::<()>()?; // explicit T = ()
    Ok(x)
}
```

ALWAYS migrate via explicit type annotations when upgrading to edition 2024. The `never_type_fallback_flowing_into_unsafe` lint is `deny` by default, catching the dangerous cases at compile time. See [[rust-syntax-edition-2024]] for the full migration checklist.

## Const generics overview

`<const N: usize>` parameterizes a type by a value, not another type. Stable for primitive integer types, `bool`, and `char`.

```rust
pub struct Matrix<const ROWS: usize, const COLS: usize> {
    data: [[f64; COLS]; ROWS],
}

impl<const R: usize, const C: usize> Matrix<R, C> {
    pub fn zero() -> Self { Self { data: [[0.0; C]; R] } }
}

let m: Matrix<3, 4> = Matrix::zero();
```

ALWAYS use const generics for fixed-size APIs (matrices, ring buffers, fixed-key crypto). NEVER attempt complex const expressions like `<const N: usize, const M: usize>` with `N + M` in bounds without the `generic_const_exprs` feature (still unstable). For const-generic depth see [[rust-syntax-generics]].

## Reference files

- [`references/methods.md`](references/methods.md) : concept surface (repr attributes table, primitive sizes, niche-eligible types, never-type rules)
- [`references/examples.md`](references/examples.md) : working code patterns (nominal typing, ZST markers, repr variants, niche optimization, type-state, const generics)
- [`references/anti-patterns.md`](references/anti-patterns.md) : 6 documented anti-patterns with root-cause analysis

## External sources

- [Rust Reference : types][ref-types]
- [Rust Reference : type-layout][ref-layout]
- [Rust Reference : special-types-and-traits][ref-special]
- [Rust Reference : never type][ref-never]
- [std::marker::PhantomData][phantom-doc]
- [std::num::NonZeroU32][nonzero-doc]
- [Edition 2024 : never-type-fallback][never-fallback]

[ref-types]: https://doc.rust-lang.org/reference/types.html
[ref-layout]: https://doc.rust-lang.org/reference/type-layout.html
[ref-special]: https://doc.rust-lang.org/reference/special-types-and-traits.html
[ref-never]: https://doc.rust-lang.org/reference/types/never.html
[phantom-doc]: https://doc.rust-lang.org/std/marker/struct.PhantomData.html
[nonzero-doc]: https://doc.rust-lang.org/std/num/type.NonZeroU32.html
[never-fallback]: https://doc.rust-lang.org/edition-guide/rust-2024/never-type-fallback.html

---
> Source: [Impertio-Studio/Rust-Claude-Skill-Package](https://github.com/Impertio-Studio/Rust-Claude-Skill-Package) — distributed by [TomeVault](https://tomevault.io).
<!-- tomevault:4.0:skill_md:2026-06-16 -->

