Browse the documentation

Preview documentation for AEL Beta 0.0.1 — launching soon

Ownership

AEL, the Agent Engineering Language, will catch ownership mistakes before anything runs: using a value after it has moved, changing a value while something else reads it, or reading a value that was never set. Checked code will have no null values and no raw pointers.

Status

Planned for AEL Beta 0.0.1. AEL is not available yet.

Copy and move

Every value will have one owner: the binding, field or collection that holds it.

  • Copied values: integers, bool, unit and shared references, and arrays, structs, enums, Option and Result made only of such values. Passing one on will leave the original usable.
  • Moved values: bounded text, lists, exclusive references, and anything that holds one of them. Passing one to a function, assigning it, returning it, putting it into a struct or a message, or binding it in a match will move it. The old place will not be usable until it receives a new value.
Preview syntax — may change before launch
fn archive(note: Str<200>) -> () {
    # archive owns note from here on
}

fn example() -> () {
    let mut note: Str<200> = "first";
    archive(note);        # note moves into archive
    # archive(note);      # would fail the check: note has moved
    note = "second";      # note has a value again
    archive(note);
}

A struct will move as a whole: in the preview, you will not be able to move one field out and keep using the rest.

When a binding receives a new value, the new value will be computed first, and only then will the old one go. If computing the new value fails, the binding will keep the old one.

Borrowing

A reference will let code use a value without taking it.

ReferenceCreated withAllows
Shared, &T&valueReading. Any number of shared references may exist at once.
Exclusive, &mut T&mut value on a let mut bindingReading and writing. While it exists, it is the only way to reach the value.
Preview syntax — may change before launch
fn read(count: &i64) -> i64 {
    return *count;
}

fn reset(count: &mut i64) -> () {
    *count = 0;
}

fn example() -> i64 {
    let mut count: i64 = 5;
    let seen: i64 = read(&count);   # shared, for the length of the call
    reset(&mut count);              # exclusive, for the length of the call
    return seen + count;
}
  • *r will read the value behind a reference. Writing through *r will need an exclusive reference.
  • A shared and an exclusive reference to the same value, or two exclusive ones, will not be able to exist at the same time, including through a function call.
  • You will not be able to move a value while a reference to it exists.

How long a borrow lasts

A borrow held in a binding will last until the end of the block that declares the binding, not merely until its last use. To end a borrow early, put it in a block of its own:

Preview syntax — may change before launch
fn example() -> i64 {
    let mut total: i64 = 1;
    {
        let view: &i64 = &total;
        let seen: i64 = *view;
    }
    total = 2;            # allowed: the borrow ended with its block
    return total;
}

A borrow passed straight into a call will last for that call.

Where references will never go

In the preview, a reference will not be able to:

  • be returned from a function;
  • be kept in an agent's state that outlives the current call;
  • be sent in a message to another agent or service;
  • be held across a wait, such as a pending request or a timer.

These rules will refuse some programs that would in fact be correct. They will be deliberately strict, so that the check never has to guess.

Definite initialization

A binding declared without a value will have to receive one on every path before it is read.

Preview syntax — may change before launch
fn choose(flag: bool) -> i64 {
    let x: i64;
    if flag {
        x = 1;
    } else {
        x = 2;
    }
    return x;
}

Leave out the else branch and the check will refuse return x;. A value set only inside a while loop will not count after the loop, because the loop may run zero times. A binding without mut will be able to receive its value exactly once.

Ownership and messages

Sending a value to another agent will move it into the receiver's mailbox. If the mailbox is full or closed, the send will hand the value back to you, so it is never lost and never owned twice. Concurrency describes mailboxes.