References

Details of how References & types work.

Immutable

Let Foo be a type.

A declaration like val my_foo: Foo = ... creates a immutable Foo, and a binding that cannot be changed.

Assigning a immutable Foo to a var is an error:

val my_foo: Foo = .new()

var copy = my_foo  // error: cannot assign a immutable value to a variable

When creating a new Foo it is saved as a immutable value:

val my_foo: Foo = Foo {
    field = 0,
    field = 1,
    field = 2,
}

When passing a Foo to a function:

  • the parameter is declared as Foo
  • a immutable reference is passed
fun do_something(foo: Foo) {
    // do something...
}

val my_foo: Foo = .new()
do_something(my_foo)

Mutable

TBD: syntax

Let Bar be a type.

A declaration like var my_bar: *Bar = ... creates a mutable Bar, and a binding that can be changed.

A mutable *Bar cannot be assigned to a val:

var my_bar: *Bar = .new()
val copy = my_bar  // This is an error

This is because a val should be immutable. If such assignment was allowed then one could do this:

use std

type Person = struct {
    name: String,
    age: i64,
}

var john = Person {
    name = "John Doe",
    age  = 32,
}

val clone = john   // mutable bind decays to a immutable one... this is not allowed
std.println(clone.age)  //= 32  - the clones value

john.age = 33      // mutate og ref

std.println(clone.age)  //= 33  - the underlying data of a `val` has changed!

TBD: clone syntax

Instead, if you want to do that you can clone the var instead:

var my_bar: *Bar = .new()

val copy = @clone(my_bar)  // Instead of a reference to the underlying data, clone the struct.
type Person = struct {
    age: i64,
}

type Family = struct {
    dad: *Person,        // immutable reference
    mom: Person,         // mutable reference
}

val f1 = Family {
    dad = Person {
        age = 32
    }
}

var f2 = @clone(f1)  // shallow clone
f2.dad.age += 1      // ok?
f2.mom.age += 1      // error: Family.mom is irreplaceable

var f2 = .{ ...f1, dad = @clone(f1.dad) }