Memory & references
Understand copies, shared object identity, and read-only or writable views.
On this page
Valk manages ordinary class and collection allocations with garbage collection. Most application code does not pair every allocation with a free. You still need to understand whether an expression copies a value or refers to existing storage.
Understand assignment#
class Counter {
value: int (0)
}
fn main() {
let counter = Counter{}
let same_counter = counter
same_counter.value++
println(counter.value) // 1
}
Both variables name the same object. Reassigning one variable would change what that variable refers to; changing the object's property is visible through both references.
Structs and fixed arrays are value types: assignment copies their fields or elements. Copying a field that holds a reference still copies the reference, not the referenced object.
Borrow a sequence#
Use a slice borrow when a function needs to read existing elements without constructing another container:
fn total(values: &[int]) int {
let result = 0
each values as value {
result += value
}
return result
}
fn main() {
let scores = Array[int]{ 10, 20, 30 }
println(total(scores.view())) // 60
}
&[int] is a read-only view of integer elements. Passing an array view to this function does not copy its elements. Use &mut [int] when the function must write to the supplied storage.
fn reset(values: &mut [int]) {
let index: uint = 0
while index < values.length {
values[index] = 0
index++
}
}
fn main() {
let scores = [int]{ 10, 20, 30 }
reset(scores)
println(scores[0]) // 0
}
The mut is part of the API contract: callers can see that a function may change their elements. A read-only borrow cannot be used to write or to call methods that mutate the borrowed data.
Distinguish a view from a copy#
values[start .. length] makes a copy. &values[start .. length] asks the collection for a view. With a writable array, the view can modify the same storage:
fn main() {
let scores = Array[int]{ 10, 20, 30 }
let middle = &scores[1 .. 1]
middle[0] = 99
println(scores[1]) // 99
}
A view has its own bounds. Do not treat an existing view as a live window into future array growth: the array may move to new storage while the view retains the storage it was created from.
For a single value, &T and &mut T express the same distinction between reading and writing. For example, use a mutable borrow of an array's struct element when you need to update the element in place rather than a copied struct value.
Close resources explicitly#
Garbage collection manages memory; it is not a scheduling mechanism for releasing a file, socket, or lock. Close external resources when the operation finishes, often with defer.
A class can define a gc_free() finalizer for cleanup during collection, but the time at which collection happens is not the point at which a local variable leaves scope. Keep timing-sensitive cleanup explicit.
For data used across threads, ordinary references are not the whole story. Read Concurrency for shared values and Lock[T]. Raw pointers and manual allocations belong in Native interoperability.