What should I do cast/2 instead of just sending it a plain message?

I know the difference between GenServer.call/2 and GenServer.cast/2, however, what is the difference between GenServer.cast/2 and send/2? Both are async, and with a cast I catch it with handle_cast/2, with a send/2 I catch it with handle_info/2. It even looks like the send/handle_info combo is simpler. Is there something I can do with cast/handle_cast but not with send/handle_info?

2 Likes

Probably cast is done from inside GenServer, while send is used for inter processes communcation.

Usually, you cast from the client side of your GenServer, so it is also inter-process. A good GenSenver encapsulate its operations so from outside the module it feels like a normal function call.

So instead of:

def doit() do
    GenServer.cast(__MODULE__, :doit)
end
...
def handle_cast(:doit, state) do
    {:noreply, do_something(state)}
end

I can:

def doit() do
    send(__MODULE__, :doit)
end
...
def handle_info(:doit, state) do
    {:noreply, do_something(state)}
end

Same thing?

wouldn’t that be intra-process ?

The client side api is ran by the client, in a process different from your server process.

Almost no difference (check the gen_server code). As a general rule, you should use the distinction as an organizational one: cast for functions where the caster is aware of your api, and send/info for cases where the sender is not aware of your api. To flip it around, use cast when the gen_server controls the inflow of information, use send/info when the gen_server is handling someone else’s messaging API.

E.g. system messages (:kill, :DOWN or nodedown) should be info messages, as should active tcp or udp connections (gen_server is subscribing to system or network messaging API), same with Phoenix.PubSub (gen_server is subscribing to the pubsub api.

If you’re not conceptually “subscribing” to an information source, it should be cast (except for calls ofc)

3 Likes

I read the gen_server code for a bit. The major difference I can find is that cast will never fail; send could fail if the name is not registered. So in the above code, if doit was called when the GenServer is not registered, the send version will crash and the cast version will be a no-op. I see both can be useful in different scenarios.

3 Likes