# `defguard` creates reusable guard expressions

Repeating the same guard logic across multiple functions? Define it once with `defguard` and use it everywhere - in function heads, `case`, `cond`, and `with` clauses.

```elixir
defmodule Guards do
  @moduledoc "Custom guards for domain-specific validation"

  defguard is_positive_integer(value)
           when is_integer(value) and value > 0

  defguard is_valid_age(age)
           when is_integer(age) and age >= 0 and age <= 150

  defguard is_valid_email_length(email)
           when is_binary(email) and byte_size(email) >= 5 and byte_size(email) <= 254
end
```

Import and use them exactly like built-in guards:

```elixir
defmodule Accounts do
  import Guards, only: [is_positive_integer: 1, is_valid_age: 1]

  def get_user(id) when is_positive_integer(id) do
    Repo.get(User, id)
  end

  def get_user(_), do: {:error, :invalid_id}

  def create_user(%{age: age} = attrs) when is_valid_age(age) do
    %User{}
    |> User.changeset(attrs)
    |> Repo.insert()
  end

  def create_user(_), do: {:error, :invalid_age}
end
```

## Why not just use regular functions?

Guards run at pattern-match time with special optimizations. Regular functions can't be used in guard position:

```elixir
# This won't compile - regular functions not allowed in guards
def is_admin?(user), do: user.role == :admin

def admin_action(user) when is_admin?(user) do  # CompileError!
  # ...
end

# This works - defguard creates a macro that expands in guard context
defguard is_admin(user) when user.role == :admin

def admin_action(user) when is_admin(user) do  # Works!
  # ...
end
```

## Works everywhere guards work

Custom guards work in all guard contexts:

```elixir
import Guards, only: [is_positive_integer: 1]

# Function heads
def process(id) when is_positive_integer(id), do: {:ok, id}

# Case expressions
case value do
  n when is_positive_integer(n) -> {:positive, n}
  n when is_integer(n) -> {:non_positive, n}
  _ -> :not_integer
end

# With expressions
with id when is_positive_integer(id) <- Map.get(params, "id") do
  get_resource(id)
end

# Anonymous functions
Enum.filter(list, fn x when is_positive_integer(x) -> true; _ -> false end)
```

## Combining guards

Build complex guards from simpler ones:

```elixir
defmodule Validations do
  defguard is_non_empty_string(value)
           when is_binary(value) and byte_size(value) > 0

  defguard is_uuid_length(value)
           when is_binary(value) and byte_size(value) == 36

  # Combine existing guards
  defguard is_valid_uuid(value)
           when is_non_empty_string(value) and is_uuid_length(value)
end
```

## Private guards with `defguardp`

Use `defguardp` for module-internal guards:

```elixir
defmodule OrderProcessor do
  # Private - only usable in this module
  defguardp is_processable_status(status)
            when status in [:pending, :confirmed, :paid]

  def process(%Order{status: status} = order) when is_processable_status(status) do
    # Process the order
  end

  def process(%Order{status: status}) do
    {:error, {:invalid_status, status}}
  end
end
```

## What's allowed in defguard

Guards must use only allowed expressions - no arbitrary function calls:

**Allowed:**

- Type checks: `is_integer/1`, `is_binary/1`, `is_map/1`, etc.
- Comparisons: `==`, `!=`, `<`, `>`, `<=`, `>=`, `===`, `!==`
- Boolean: `and`, `or`, `not`
- Arithmetic: `+`, `-`, `*`, `/`, `abs/1`, `rem/2`
- Binary/tuple: `byte_size/1`, `tuple_size/1`, `elem/2`
- Other guards: `in`, `is_nil/1`, `length/1`, `map_size/1`

**Not allowed:**

- Custom functions
- Side effects
- Exception-raising code

```elixir
# Won't work - String.contains? isn't a guard-safe function
defguard has_at_sign(email) when String.contains?(email, "@")  # CompileError!

# Works - use guard-allowed operations instead
defguard has_minimum_length(str, min) when is_binary(str) and byte_size(str) >= min
```

`defguard` has been available since Elixir 1.6. If you're copy-pasting the same guard expressions across your codebase, extract them into named guards for cleaner, more maintainable code.

**Links:**

- [Elixir Patterns and Guards](https://hexdocs.pm/elixir/main/patterns-and-guards.html)
- [Kernel.defguard/1](https://hexdocs.pm/elixir/Kernel.html#defguard/1)


---

Created by: almirsarajcic
Date: December 24, 2025
URL: https://elixirdrops.net/d/CJv97pHE
