the actor model and the mailbox
Picture a company where employees never reach into each other's desks. To get something done you send a memo to a colleague's inbox; they process their inbox one memo at a time, do the work on their own private files, and send memos back. Nobody shares a desk, so there is no fighting over who holds the stapler. The actor model is concurrency built entirely this way: the unit is an actor, a small isolated entity that owns its private state and communicates only by sending and receiving messages — never by touching another actor's memory.
Each actor has a mailbox: a queue where incoming messages land. The actor processes messages from its mailbox strictly one at a time, in sequence. This single-consumer rule is the whole trick — because an actor handles only one message at once, its private state is never touched concurrently, so there are no data races inside it and it needs no locks at all. To affect another actor, you send it a message (asynchronously: send and move on); the recipient eventually pulls it from its mailbox and reacts — updating its own state, sending more messages, or spawning new actors. This is message passing rather than shared memory: there is no shared mutable state to protect, only messages flowing between isolated actors. Often actors enjoy location transparency — you send to an actor's address the same way whether it lives in this process or on a machine across the network, which is how Erlang/Akka systems scale across nodes.
Actors matter because they make a hard problem (safe concurrency) tractable by removing its cause: no shared mutable state means no locks, no data races, no deadlocks from lock ordering. They underpin Erlang/Elixir (telecom-grade reliability), Akka, and Orleans. The framing 'do not communicate by sharing memory; share memory by communicating' (from Go's channels, a close cousin) captures the spirit. Honest caveats: actors trade lock bugs for new ones — unbounded mailboxes can grow without limit under overload (needing backpressure), message ordering between different senders is not guaranteed, and a request/reply round-trip is more awkward than a function call. It removes shared-memory hazards; it does not remove the need to think about concurrency.
A counter actor: its only state is 'count'. Messages: Inc, Get(reply_to). It processes one at a time, so 'count += 1' never races. To read it you send Get and await a reply message — no lock, no shared variable.
One mailbox, one consumer: serial message processing makes the actor's private state race-free without any lock.
Actors remove data races but not all concurrency problems: mailboxes can overflow under load (apply backpressure), message ordering across different senders is not guaranteed, and request/reply is clumsier than a call. 'No shared memory' is the win; it is not a free lunch.