Search code examples
elixirgen-server

Can a GenServer Have Its Own Struct in Elixir?


Scenario:

  • I have a simple GenServer for managing some state.
  • Currently, I am using map to manage my state. But it is growing as I am adding more data to the state.

Problem:

  • So, to have some compile-time guarantee, can I have a struct in my GenServer module?
  • And, if yes, is it a right approach?
  • If not, what are the alternatives?

Solution

  • Just declare a normal struct (optionally in a module nested in your GenServer namespace) and use it as an initial state:

    defmodule Test do
      defmodule State do
        defstruct ~w|foo bar baz|a
      end
    
      use GenServer
    
      def start_link(opts \\ []) do
        GenServer.start_link(__MODULE__, %State{foo: 42, bar: opts}, name: __MODULE__)
      end
    
      @impl true
      def init(opts \\ []), do: {:ok, opts}
    
      def state, do: GenServer.call(__MODULE__, :state)
    
      @impl true
      def handle_call(:state, _from, %State{} = state) do
        {:reply, state, state}
      end
    end
    
    with {:ok, _} <- Test.start_link(pi: 3.14) do
      IO.inspect Test.state, label: "State"
    end
    #⇒ State: %Test.State{bar: [pi: 3.14], baz: nil, foo: 42}