Docs/Memory

Copies & references

When a value is copied, shared, or cloned.

On this page

Memory is managed for you: objects are freed once nothing uses them. What you choose is whether a value is shared or copied.

Assignment shares objects#

Class values, including String, Array and HashMap, are references. Assigning one or passing it to a function shares the same object:

Valk
class Counter {
    value: int (0)
}

fn bump(counter: Counter) {
    counter.value++
}

fn main() {
    let a = Counter{}
    let b = a
    b.value++
    bump(a)
    println(a.value) // 2
}

Assigning a new object to b only changes what b refers to; a is untouched.

Values are copied#

Structs, numbers, bool, enums and fixed arrays such as [int x 3] are copied on assignment:

Valk
fn main() {
    let a: [int x 3] = { 1, 2, 3 }
    let b = a
    b[0] = 99
    println(a[0]) // 1
    println(b[0]) // 99
}

A copied struct field that holds an object still refers to the same object.

Copy or clone an object#

Expression Result
copy x a new object with the same property values; objects inside are shared
clone x a new object, and everything it refers to copied too
Valk
class Team {
    name: String
    members: Array[String] (.{})
}

fn main() {
    let a = Team { name: "red" }
    let b = copy a
    let c = clone a
    b.members.append("Ada")
    println(a.members.length) // 1
    println(c.members.length) // 0

    let list = Array[int]{ 1, 2 }
    let other = copy list
    other.append(3)
    println(list.length)      // 2
}

Collections define their own copy: copy list is a new list with the same items. See Classes to control how your own class is copied.

A range copies, a view shares#

x[start .. length] copies part of a collection. &x[start .. length] is a view of the same elements:

Valk
fn main() {
    let scores = Array[int]{ 10, 20, 30 }
    let part = scores[1 .. 2]
    let view = &scores[1 .. 2]
    part[0] = 1
    view[0] = 99
    println(scores[1]) // 99
}

A view keeps the storage it was made from. After the array grows, the two may no longer see each other's changes.

Close resources yourself#

Freeing memory is automatic, but files, sockets and connections should be closed when you are done. defer runs the close when the function returns:

Valk
use valk.fs

fn main() {
    fs.write("example.txt", "hello") ! return
    defer fs.delete_file("example.txt") ! {}

    let file = fs.stream("example.txt") ! return
    defer file.close() ! {}
    let buffer: [u8 x 16] = { 0... }
    let length = file.read(&buffer) ! return
    println(length) // 5
}

Do not rely on an object being collected at a particular time. A class's gc_free() finalizer is only a fallback; see Classes.