Skip to content

ARC + Copy-on-Write (CoW)

One of Wysna’s defining engineering principles is Pure Value Semantics powered by Automatic Reference Counting (ARC) combined with Copy-on-Write (CoW).

Traditional programming languages force a difficult trade-off:

  • Tracing Garbage Collectors (Go, Java, C#): Introduce periodic background scans, unpredictable pauses, and high memory footprints, making them less suitable for hard real-time systems, embedded devices, or tight WebAssembly sandbox limits.
  • Manual Allocation (C, C++): Fast and compact, but leads to use-after-free, double-free, and memory leak vulnerabilities.
  • Strict Lifetime / Borrow Checkers (Rust): Eliminate GC without memory unsafety, but require developers to annotate lifetimes, navigate complex borrow checkers, and restructure code around single-owner rules.

In Wysna, every complex data structure (str, list, dict, and struct) acts logically as an independent value, while physically sharing underlying memory buffers until a mutation occurs.

Step 1: Assignment (let b = a)
┌──────────────┐
│ Variable a ├──┐
└──────────────┘ │ ┌──────────────────────┐
├──────►│ Shared Data Buffer │ (refcount = 2)
┌──────────────┐ │ │ [ 10, 20, 30 ] │
│ Variable b ├──┘ └──────────────────────┘
└──────────────┘
Step 2: Mutation on b (b.append(40)) -> CoW triggers!
┌──────────────┐ ┌──────────────────────┐
│ Variable a ├─────────►│ Original Buffer │ (refcount = 1)
└──────────────┘ │ [ 10, 20, 30 ] │
└──────────────────────┘
┌──────────────┐ ┌──────────────────────┐
│ Variable b ├─────────►│ Cloned Buffer │ (refcount = 1)
└──────────────┘ │ [ 10, 20, 30, 40 ] │
└──────────────────────┘

When you pass a list to a function or assign it to another variable:

let original = [10, 20, 30]
let copy = original # Increments refcount. No buffer allocation occurs!

The underlying buffer is shared, and its reference count increments atomically or locally.

When a variable attempts to mutate a shared buffer:

let mut updated = copy
updated[0] = 99 # Checks refcount: > 1, so it allocates a fresh buffer!
  • If refcount == 1: The mutation occurs in place, with zero allocation overhead.
  • If refcount > 1: The runtime clones the buffer, decrements the original’s reference count, and applies the mutation exclusively to the new copy.

struct DatabaseConfig {
host: str
port: int
}
fn main() -> unit {
let prod_cfg = DatabaseConfig {
host: "prod.internal",
port: 5432,
}
let mut staging_cfg = prod_cfg
staging_cfg.host = "staging.internal"
# Both configurations are completely isolated:
print("Prod Host: ", prod_cfg.host) # prod.internal
print("Staging Host:", staging_cfg.host) # staging.internal
}
  1. Zero Garbage Collection Stalls: No stop-the-world pauses.
  2. Immediate Destruction: Memory is reclaimed the microsecond its owner variable exits scope.
  3. No Dangling Pointers: The compiler guarantees that pointers are managed exclusively by the runtime runtime primitives.