Skip to main content
๐Ÿ“œ WAYPOINT LESSON

Managed, unmanaged, unsafe: the boundary

โญโญโญ advancedโณ 15 min read๐Ÿ“ Lesson 231 of 265

The three worlds and their rules โ€” what unsafe code actually lifts, and what it never lifts.

Managed, unmanaged, unsafe: the boundary

Three adjectives, three different things:

  • Managed: memory the GC owns and can move; code the runtime verifies. All ordinary C#.
  • Unmanaged: memory the GC does not control (native malloc, OS handles, file mappings). Reached via interop APIs.
  • Unsafe: code regions where the compiler permits pointer operations โ€” required only when you want C-style access INSIDE the managed world (or at its edge). Unsafe does not mean unmanaged: most unsafe code operates on perfectly managed arrays.
unsafe int Sum(int[] values)
{
    fixed (int* p = values)   // pin: promise the GC not to move it while scoped
    {
        int s = 0;
        for (int i = 0; i < values.Length; i++) s += p[i];
        return s;
    }   // unpin here โ€” the pointer is invalid after this line
}

What unsafe lifts: pointer types (int*), &address-of, -> member access, stackalloc into pointers, direct indexing through pointers. What it NEVER lifts: memory safety of the GC's contract (you can corrupt anything you can address โ€” that is the price), type-system guarantees (pointer reinterpretation is on you), or sandbox rules (Code Journey allows unsafe code INSIDE its hardened container; the container is the boundary that matters).

The discipline this course demands: unsafe blocks are small, scoped, and non-escaping. Pointers never become fields, never cross await, never outlive their fixed. If you cannot draw the box around the unsafe region, the design is wrong. Span<T> (Module 2) covers most of what unsafe used to be needed for โ€” reach for unsafe when spans genuinely cannot express it.