# V6003 resource_escape

URL: /docs/errors/V6003
Markdown: /docs/errors/V6003.md

> A @resource value (v0: File) escapes the callback of the operation that introduced it: the callback returns a type containing it (directly, or inside a record, list or closure), or the value is stored in handler var state (design §8.4, §17.70).

An error from the `capability` phase. `sakura explain V6003` prints this explanation offline.

A `@resource` value (v0: `File`) escapes the callback of the operation that introduced it: the
callback returns a type containing it (directly, or inside a record, list or closure), or the
value is stored in handler `var` state (design §8.4, §17.70). The runtime closes the file when
the callback returns, so an escaped `File` cannot be used. Read or write inside the callback and
return ordinary data. Use-at-most-once is not checked statically in v0.

Wrong:

```sakura
import std/fs.{Fs, File, Mode, FsError}
import std/path.{Path}
fn keep_open(p: Path) -> File ! {Fs, Fail<FsError>} {
  Fs.with_file(p, Mode.Read, fn(file) { file })
}
```

Fixed:

```sakura
fn first_kb(p: Path) -> Bytes ! {Fs, Fail<FsError>} {
  Fs.with_file(p, Mode.Read, fn(file) { Fs.read_chunk(file, 1024) })
}
```

## Wrong program [#wrong-program]

[`compiler/tests/ui/V6003_resource_escape.sakura`](https://github.com/icantcodefyi/sakuralang/blob/master/compiler/tests/ui/V6003_resource_escape.sakura), which the compiler's ui suite runs through `sakura check`:

```sakura
import std/fs.{File, Fs, FsError, Mode}
import std/path.{Path}

fn keep_open(p: Path) -> File ! {Fs, Fail<FsError>} {
  Fs.with_file(p, Mode.Read, fn(file) { file })
}
```

`sakura check --json` reports this record (pretty-printed; `$DIR` is the fixture's directory):

```json
{
  "schema_version": "1.0",
  "kind": "diagnostic",
  "id": "d:9a99a69a967c1b07",
  "code": "V6003",
  "severity": "error",
  "phase": "capability",
  "message": "this `File` escapes the callback of `with_file`: it holds a resource, which is closed when the callback returns (design §8.4)",
  "snapshot": "sha256:$SNAPSHOT",
  "symbol_id": "main::V6003_resource_escape::keep_open",
  "spans": [
    {
      "uri": "file://$DIR/V6003_resource_escape.sakura",
      "content_hash": "sha256:579bb06c8d05fa88c34b60f174de613fd71825550c967a76fdccb4e0dd20580f",
      "start_byte": 216,
      "end_byte": 220,
      "role": "primary"
    }
  ],
  "related_ids": [],
  "expected": null,
  "actual": null,
  "obligation": null,
  "counterexample": null,
  "explanation_uri": "sakura://explain/V6003",
  "fixes": []
}
```

More fixtures for this code: [`compiler/tests/ui/V6003_route_handler_returns_file.sakura`](https://github.com/icantcodefyi/sakuralang/blob/master/compiler/tests/ui/V6003_route_handler_returns_file.sakura).

## Fixed program [#fixed-program]

[`compiler/tests/ui/V6003_resource_escape.fixed.sakura`](https://github.com/icantcodefyi/sakuralang/blob/master/compiler/tests/ui/V6003_resource_escape.fixed.sakura), which `sakura check` accepts with no diagnostics:

```sakura
import std/fs.{Fs, FsError, Mode}
import std/path.{Path}

fn first_kb(p: Path) -> Bytes ! {Fs, Fail<FsError>} {
  Fs.with_file(p, Mode.Read, fn(file) { Fs.read_chunk(file, 1024) })
}
```
