Disposables: Factory Functions

This functionality requires HHVM 3.24 or later.

In general, disposables should not be returned, and trying to do so is a typechecker error:

<?hh

namespace Hack\UserDocumentation\Examples\Intro\Returning\TypeError;

class Handle implements \IDisposable {
  public function __dispose(): void {
  }
}

function foo() {
  return new Handle();
}

Output

type-error.php:11:10,21: 'new' must not be used for disposable objects except in a 'using' statement (Typing[4187])

In some cases - factory functions in particular - returning a disposable is appropriate. In these cases, the function needs to be marked as <<__ReturnDisposable>>:

<?hh

namespace Hack\UserDocumentation\Examples\Intro\Returning\Simple;

class Handle implements \IDisposable {
  public function __construct(
    private string $id,
  ) {
  }

  public function __dispose(): void {
    \printf("Releasing %s.\n", $this->id);
  }
}

class Foo {
  private string $id;
  public function __construct(
  ) {
    $this->id = 'opaque internal handle';
  }
  
  <<__ReturnDisposable>>
  public function getHandle(): Handle{
    return new Handle($this->id);
  }
}

function main() {
  $foo = new Foo();
  using ($handle = $foo->getHandle()) {
    print("doing stuff\n");
  }
}

main();
Output
doing stuff
Releasing opaque internal handle.

Async factory functions are also supported via using await:

<?hh

namespace Hack\UserDocumentation\Examples\Intro\Returning\Async;

class Handle implements \IDisposable {
  public function __construct(
    private string $id,
  ) {
  }

  public function __dispose(): void {
    \printf("Releasing %s.\n", $this->id);
  }
}

class Foo {
  private string $id;
  public function __construct(
  ) {
    $this->id = 'opaque internal handle';
  }
  
  <<__ReturnDisposable>>
  public async function getHandleAsync(): Awaitable<Handle> {
    return new Handle($this->id);
  }
}

async function main() {
  $foo = new Foo();
  using ($handle = await $foo->getHandleAsync()) {
    print("doing stuff\n");
  }
}

\HH\Asio\Join(main());