Packages

Mojo NIFs for OTP 28. Generated ABI, checked terms, resources, dirty schedulers, and a Mix compiler. Mojo 1.1.0 is required at build time and is not vendored.

Current section

Files

Jump to
mojoler docs tutorial 08-safe-conversion.md
Raw

docs/tutorial/08-safe-conversion.md

# Unit 8 — Safe conversion design

## Objectives

State the policy for integers, floats, atoms, binaries, lists, and maps, and point at the test that locks it.

## Why

A NIF that accepts every term will create atoms without bound or treat a bitstring as bytes. Both are OTP footguns.

## Model

```text
enif_get_* fails  ->  ArgumentError     (bad argument)
value is in range but the operation is not  ->  {:error, reason}
literal atom only ->  the error reason
```

## Theory

Integers: the width is the `enif_get_long` / `enif_get_ulong` / `enif_get_int` / `enif_get_uint` that the function calls. Just outside the 64-bit range is a bignum and becomes badarg. Negative-to-unsigned is badarg.

Floats: `enif_get_double` does not accept an integer. This OTP will not build a NaN with `0.0 / 0.0`. It raises `ArithmeticError`. The NIF's `n != n` branch is defensive and was not fed a term.

Atoms: `echo_atom` compares against five literals. `:other` is `{:error, :unknown_atom}`. The error atom is also a literal.

Lists: `enif_get_list_length` fails on an improper list. Length above 64 is `:too_long`. A nested list is `:bad_element` (depth 1). The walk is a loop.

Maps: missing `n` is `:missing`. Extra keys are dropped. That is the unknown-field policy, and the test checks `%{n: 7, extra: 1}`.

## Lab

```sh
mix test test/term_test.exs
```

## Expected

The integer edges, the overflow tuple, the atom vocabulary, the sub-binary copy, and the improper list all pass.

## Code

`native/term.mojo`. `fits_int64` is the same overflow idea as `poc.mojo`: the check itself stays inside `c_long`.

## Failure

Returning `enif_make_atom(env, user_bytes)` would let a caller create atoms. The code does not take that path. If a new error reason is needed, add a literal and a test.

## Exercise

What does `copy_binary(<<1::7>>)` return, and why?

## Answer

`{:error, :not_binary}`. `enif_inspect_binary` rejects a bitstring that is not a whole number of bytes. The NIF does not pad it.