Mailboxes are implemented with a combination of queues and semaphores.
The key feature of a mailbox is that it guarantees a test of the size of the queue with a push and pop of data as an atomic operation. That means if multiple threads are blocked waiting to push data onto a full queue using the mailbox put() method, as soon as space becomes available for a single piece of data, only thread will see that space is available and be allowed to push data onto the queue. Without that atomic guarantee, another thread could push data onto the queue in the time that your thread checked the size of the queue and pushed data onto the queue.
Similar for the get() method, if more than one thread is trying to pop data off the queue, only one thread will see that there is data queue and pop the data off the queue, the other threads will wait their turn in FIFO order.
If there is only one thread doing put()s and only one other thread doing gets() to a mailbox, you really don't need the extra overhead of a mailbox, but it is convenient to use the put() and get() methods of a mailbox that combine the test and set operations on the queue.