Parameter Passing

Here's something cool about Coco: you don't have to think about whether to pass by value or by reference. The compiler figures it out for you based on the type.

The Smart Default

Coco's rule is simple:

  • Small types (integers, floats, booleans) → passed by value (copied)
  • Large types (strings, arrays, structs) → passed by reference automatically
fn process_number($x i32) {
    // $x is a copy - modifications don't affect the original
    $x += 1;
    echo $x;
}

fn process_text($text string) {
    // $text is automatically a reference
    // Efficient even for huge strings
    echo $text.len();
}

You write the same syntax, but the compiler does the right thing. No performance penalty for passing large data, no surprise mutations.

Why This Design?

In most languages, you have to choose:

  • Pass by value — Safe (no aliasing), but expensive for large data
  • Pass by reference — Efficient, but you can accidentally mutate shared data

Coco's approach gives you both: efficiency by default, safety through the borrow checker.

fn analyze($data Dataset) {
    // $data is passed by reference automatically
    // But it's immutable unless we ask for &mut

    $total = $data.sum();
    $average = $total / $data.len();
    // ...
}

$my_data = load_dataset("big_file.csv");
analyze($my_data);
// $my_data is unchanged

Explicit References

Sometimes you need explicit control. Use & for immutable references and & with mutation:

// Explicitly take a reference
fn count_words($text &string): i32 {
    return $text.split(" ").len();
}

// Take a mutable reference
fn append_suffix($text &string, $suffix string) {
    $text = $text + $suffix;
}

$message = "Hello";
append_suffix(&$message, "!");
echo $message;  // Hello!

Ownership Transfer

When you want to take ownership (consume the value), use move:

fn consume($data move Vec<i32>) {
    // $data is moved into this function
    // Caller can no longer use it
    process_and_free($data);
}

$numbers = vec![1, 2, 3];
consume($numbers);
// $numbers is no longer valid here

This is useful when:

  • The function will free the resource
  • You want to prevent the caller from using the data afterward
  • You're transferring ownership to another data structure

Copy vs Move Semantics

Types in Coco are either Copy or Move:

Copy types (small, stack-allocated):

  • Integers, floats, booleans, chars
  • Passed by value, always copied

Move types (heap-allocated or large):

  • Strings, vectors, structs with heap data
  • Passed by reference by default
  • Can be explicitly moved
// i32 is Copy
$a = 5;
$b = $a;  // $a is copied
echo $a;  // Still valid

// String is Move
$s1 = "hello";
$s2 = $s1;  // $s1 is moved
// echo $s1;  // Error: $s1 was moved

The Borrow Checker

Coco's borrow checker enforces these rules at compile time:

  1. One mutable reference OR any number of immutable references
  2. References must be valid (no dangling pointers)
$data = vec![1, 2, 3];

// Multiple immutable borrows - OK
$a = &$data;
$b = &$data;
echo $a.len(), $b.len();

// Mutable borrow - can't have other borrows
$c = &$data;  // Mutable
// let $d = &$data;  // Error: can't borrow while mutably borrowed
$c.push(4);

This catches data races at compile time.

Parameter Modes Summary

Syntax Mode Use When
$x Type Auto (value or ref) Default - let compiler decide
$x &Type Immutable reference Need explicit reference
$x &Type (mut) Mutable reference Need to modify the value
$x move Type Ownership transfer Consuming the value

Returning Values

The same principles apply to return values:

// Small values returned by value (copied)
fn calculate(): i32 {
    return 42;
}

// Large values use move semantics
fn build_report(): Report {
    $report = Report::new();
    // ... build it ...
    return $report;  // Moved out, not copied
}

Returning large values is efficient—no copying happens.

Zero-Copy Views

For cases where you need to look at data without copying:

fn first_word($text &string): &string {
    // Return a reference to part of the input
    $end = $text.find(' ').unwrap_or($text.len());
    return &$text[0..$end];
}

$sentence = "Hello World";
$word = first_word(&$sentence);
echo $word;  // Hello

The returned reference is valid as long as $sentence is valid.

Best Practices

Let the compiler decide. Use the simple $x Type syntax unless you have a specific reason for explicit references.

Use mutable references sparingly. Prefer returning new values over mutating in place.

Think about ownership. If a function needs to keep data, it should take ownership. If it just needs to look at it, take a reference.

Document ownership transfer. If a function consumes its input, make that clear in the name or docs.

// Clear from the name that this takes ownership
fn into_bytes($s move string): Vec<u8> {
    // ...
}
Copyright (c) 2025 Ocean Softworks, Sharkk