Functions: Inout Parameters

Hack functions normally pass arguments by value. inout provides "copy-in, copy-out" arguments, which allow you to modify the variable in the caller.

function takes_inout(inout int $x): void {
  $x = 1;
}

function call_it(): void {
  $num = 0;
  takes_inout(inout $num);

  // $num is now 1.
}

This is similar to copy-by-reference, but the copy-out only happens when the function returns. If the function throws an exception, no changes occur.

function takes_inout(inout int $x): void {
  $x = 1;
  throw new Exception();
}

<<__EntryPoint>>
function call_it(): void {
  $num = 0;
  try {
    takes_inout(inout $num);
  } catch (Exception $_) {
  }

  // $num is still 0.
}

inout must be written in both the function signature and the function call. This is enforced in the typechecker and at runtime.

Indexing with inout

In addition to local variables, inout supports indexes in value types.

function set_to_value(inout int $item, int $value): void {
  $item = $value;
}

function use_it(): void {
  $items = vec[10, 11, 12];
  $index = 1;
  set_to_value(inout $items[$index], 42);

  // $items is now vec[10, 42, 12].
}

This works for any value type: vec, dict, keyset or array. You can also do nested access e.g. inout $foo[$y][z()]['stuff'].

Dynamic Usage

inout is a different calling convention, so dynamic calls will not work with inout parameters. For example, you cannot use meth_caller, call_user_func or ReflectionFunction::invoke.

unset on a inout parameter will set the value to null. This is not recommended, and will raise a warning when the function returns.

References (deprecated)

Hack used to support references in partial mode files.

function set_to_zero(int &$x): void {
  $x = 0;
}

References allowed multiple mutable accesses to the same value. This prevented HHVM relying on copy-on-write behaviors of value types, making it slower.

It was also possible to confuse the typechecker (which assumed variable wouldn't change types across function calls) and readers of your code (especially when using async or __Memoize).

Migrating to inout

HHVM allows references and inout parameters to be used interchangeably. This allows you to gradually migrate your code.

inout parameters do not support default values, so you will need to split up functions that have optional references.

function call_foo(int $x, int &$result = null): void {
  $foo_result = foo($x);
  
  // If provided, save the output to $result.
  if ($result !== null) {
    $result = $foo_result;
  }
}

Instead, write a separate function that makes the parameter required.

function call_foo(int $x): void {
  foo($x);
}

function call_foo_save_result(int $x, inout int $result): void {
  $foo_result = foo($x);
  $result = $foo_result;
}

This is how built-in functions like preg_match were migrated.