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.
// 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
References &
The ampersand (&) creates a borrowed reference. References allow access to data without taking ownership.
// 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 worksReference Parameters
Functions commonly use references to avoid unnecessary copies:
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
&) 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.
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()); // 43Copy Parameters
Copy parameters are useful when a function needs to modify a value without affecting the caller:
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()); // 20Ownership in Classes
Class properties declare their ownership mode:
[ 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:
// 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 itOwnership Rules Summary
| Modifier | Semantics | Use When |
|---|---|---|
^ | Transfer ownership (move) | Storing data, factory methods, taking ownership |
& | Borrow reference (no ownership) | Read-only access, avoiding copies |
% | Create independent copy | Small values, need to modify locally |
Warning
Next Steps
Learn how ownership works with generic types or see common ownership patterns in practice.