Ownership

XXML uses explicit ownership semantics to ensure memory safety without garbage collection. Every value has exactly one owner at any time.

Ownership Modifiers

XXML has three ownership modifiers that define how values are passed and stored:

^

Owned

Transfer ownership

&

Reference

Borrow without ownership

%

Copy

Create independent copy

Owned Values ^

The caret (^) indicates ownership. When a value is owned, the variable is responsible for its memory and the value is destroyed when the owner goes out of scope.

owned.xxml
// Create an owned string
Instantiate String^ As <name> = String::Constructor("Alice");

// Ownership is transferred to 'other'
Instantiate String^ As <other> = name;

// ERROR: 'name' is no longer valid - ownership was moved
// Run Console::printLine(name);  // Compile error!

// 'other' now owns the string
Run Console::printLine(other);

Note

When ownership is transferred (moved), the original variable becomes invalid. This prevents use-after-move bugs at compile time.

References &

The ampersand (&) creates a borrowed reference. References allow access to data without taking ownership.

reference.xxml
// Create an owned string
Instantiate String^ As <name> = String::Constructor("Alice");

// Borrow a reference - 'name' still owns the data
Instantiate String& As <nameRef> = &name;

// Both are valid
Run Console::printLine(name);      // Original still valid
Run Console::printLine(nameRef);   // Reference also works

Reference Parameters

Functions commonly use references to avoid unnecessary copies:

xxml
Method <printPerson> Returns None Parameters (
    Parameter <name> Types String&,
    Parameter <age> Types Integer&
) Do
{
    Run Console::printLine(name);
    Run Console::printLine(age.toString());
}

// Caller retains ownership
Instantiate String^ As <myName> = String::Constructor("Bob");
Instantiate Integer^ As <myAge> = Integer::Constructor(30);

Run printPerson(&myName, &myAge);

// Variables still valid after the call
Run Console::printLine(myName);

Tip

Use references (&) for read-only access to large data structures. This avoids expensive copies while keeping the caller's ownership intact.

Copies %

The percent sign (%) creates a copy. The new value is completely independent of the original.

copy.xxml
Instantiate Integer^ As <original> = Integer::Constructor(42);

// Create a copy - 'copy' gets its own value
Instantiate Integer^ As <copy> = %original;

// Modifying copy doesn't affect original
Set copy = copy.add(Integer::Constructor(1));

Run Console::printLine(original.toString());  // 42
Run Console::printLine(copy.toString());      // 43

Copy Parameters

Copy parameters are useful when a function needs to modify a value without affecting the caller:

xxml
Method <processValue> Returns Integer^ Parameters (
    Parameter <value> Types Integer%  // Receives a copy
) Do
{
    // Can freely modify the copy
    Set value = value.multiply(Integer::Constructor(2));
    Return value;
}

Instantiate Integer^ As <x> = Integer::Constructor(10);
Instantiate Integer^ As <result> = processValue(x);

Run Console::printLine(x.toString());      // 10 (unchanged)
Run Console::printLine(result.toString()); // 20

Ownership in Classes

Class properties declare their ownership mode:

person.xxml
[ Class <Person> Final Extends None
    [ Private <>
        Property <name> Types String^;    // Owned
        Property <age> Types Integer^;    // Owned
    ]

    [ Public <>
        Constructor Parameters (
            Parameter <n> Types String^,   // Takes ownership
            Parameter <a> Types Integer%   // Receives copy
        ) ->
        {
            Set name = n;
            Set age = a;
        }

        // Returns reference - caller doesn't take ownership
        Method <getName> Returns String& Parameters () Do
        {
            Return &name;
        }

        // Returns copy - caller gets independent value
        Method <getAge> Returns Integer^ Parameters () Do
        {
            Return %age;
        }
    ]
]

Lambda Captures

Lambdas can capture variables with different ownership semantics:

xxml
// Copy capture - lambda gets its own copy
Instantiate Integer^ As <multiplier> = Integer::Constructor(5);
Instantiate F(Integer^)(Integer&)^ As <multiply> = [ Lambda [%multiplier] Returns Integer^ Parameters (
    Parameter <n> Types Integer&
) {
    Return n.multiply(multiplier);
}];
// multiplier still valid here

// Owned capture - moves value into lambda
Instantiate String^ As <data> = String::Constructor("secret");
Instantiate F(String^)()^ As <getData> = [ Lambda [^data] Returns String^ Parameters () {
    Return data;
}];
// data is no longer valid - ownership moved to lambda

// Reference capture - borrows from outer scope
Instantiate Integer^ As <counter> = Integer::Constructor(0);
Instantiate F(Integer^)()^ As <getCount> = [ Lambda [&counter] Returns Integer^ Parameters () {
    Return counter;
}];
// counter still owned here, lambda borrows it

Ownership Rules Summary

ModifierSemanticsUse When
^Transfer ownership (move)Storing data, factory methods, taking ownership
&Borrow reference (no ownership)Read-only access, avoiding copies
%Create independent copySmall values, need to modify locally

Warning

Ownership errors are caught at compile time. The compiler ensures that you never use a moved value or have dangling references.

Next Steps

Learn how ownership works with generic types or see common ownership patterns in practice.