skip to content

In Ruby, why does BasicObject define __send__ when Kernel already provides send, and when should library code call it?

level: middleimportance: nice to knowfreq 24%

answer

  1. send is a common word
  2. sockets transmit with send
  3. BasicObject lacks Kernel
  4. redefining warns: serious problems
  5. same visibility as send

basics

~20 s

send behaves like send but lives on BasicObject under a name nobody overrides by accident. Library code that dispatches on objects it does not control uses it, because a class may define its own send and BasicObject subclasses have no Kernel#send.

solid answer

~40 s

`send` is an ordinary word, so classes redefine it: `UDPSocket#send` transmits a datagram, and a mailer or message class often has its own `send`. `BasicObject` subclasses, typical for proxies, do not include `Kernel` and have no `send` at all. `__send__` is defined on `BasicObject`, behaves exactly like `send` (it also reaches private methods), and CRuby warns if you redefine or remove it. So generic code, such as a delegation library, a serializer or a proxy forwarding to its target, calls `__send__` to be sure it reaches Ruby's dispatcher. It is a robustness choice, not a safety one: it adds no visibility check, and application code still defaults to `public_send`.

go deeper

for a junior

Remember that send does what send does, and that sockets and some domain classes define their own send.

for a middle

Explain why generic code picks send: overridden send methods and BasicObject subclasses that have no Kernel methods at all.

for a senior

Show that you know send is robustness, not safety: it still bypasses visibility, so dispatch on untrusted names needs its own guard.

for a principal

Treat the underscored name as a library-author convention and decide where the team's generic helpers should rely on it.

## Three names, two behaviours Ruby offers three ways to call a method by name: | Method | Defined in | Visibility check | |---|---|---| | `send` | `Kernel` | none: reaches private and protected | | `__send__` | `BasicObject` | none: same behaviour as `send` | | `public_send` | `Kernel` | public methods only | `send` and `__send__` behave the same; CRuby even implements both with the same optimized dispatch. The reason there are two names is **robustness**, not behaviour. The core documentation for `send` says it directly: `__send__` is safer than `send` when the object has its own method called `send`, and it names `Socket` as the example. ## Why a second name is needed `send` is an ordinary English verb, and ordinary method names get reused: - **Sockets.** `BasicSocket#send` and `UDPSocket#send` transmit data. On a socket, `sock.send("hi", 0)` sends bytes over the network; it does not call a method named `hi`. - **Domain classes.** A `Mailer`, `Notification` or `Message` class often defines `send` for its own purpose, overriding `Kernel#send` for its instances. - **Proxies built on `BasicObject`.** `BasicObject` is the minimal root class and does not include `Kernel`, so its subclasses have no `send` and no `public_send`. They do have `__send__`, because `BasicObject` defines it. Generic code, such as a serializer, a test helper, a delegation library or a metaprogramming utility, cannot know which of these objects it will receive. If it writes `obj.send(name)`, it may transmit a datagram or trigger a domain action instead. Writing `obj.__send__(name)` avoids that, because nobody defines `__send__` by accident. ## Ruby protects the underscored name CRuby treats `__send__` as special, alongside `object_id` and `__id__`. Redefining it prints `warning: redefining '__send__' may cause serious problems`, and removing or undefining it prints a similar warning. Library authors rely on that convention: the name is reserved in practice, so a call through it reaches the real dispatcher. ```ruby require "socket" sock = UDPSocket.new sock.send("ping", 0, "127.0.0.1", 9000) # UDPSocket#send: a datagram goes out sock.__send__(:closed?) # dynamic call to IO#closed? => false class Proxy < BasicObject def initialize(target) = @target = target def method_missing(name, *args, &block) @target.__send__(name, *args, &block) end def respond_to_missing?(name, include_private = false) = true end ``` Inside `Proxy`, calling `send` on the proxy itself would not reach `Kernel#send` (there is none); it would fall into the proxy's own `method_missing`. ## What __send__ does not change 1. **It does not add a visibility check.** Like `send`, it reaches private methods. There is no underscored twin of `public_send`, so generic code that must respect visibility calls `public_send` and accepts that a class could, in principle, override it. 2. **It is not faster.** Both names go through the same dispatch path. 3. **It is not a different lookup.** The target is found through the normal method lookup, and `method_missing` runs for unknown names in both cases. ## A quick way to remember it - `send` — a common verb; any class may take it for itself. - `__send__` — the same dispatcher under a reserved-looking name, defined on the root class. - `public_send` — the dispatcher with a visibility check, defined next to `send` in `Kernel`. When you write code that receives arbitrary objects, reach for the name that cannot have been taken: `__send__` when private access is acceptable, `public_send` when it is not and the objects are ordinary `Object` descendants. ## Style in application code In everyday application code, `public_send` is the usual choice and `send` appears where private access is intended. RuboCop has a `Style/Send` cop that prefers `__send__` or `public_send` over `send`, on the grounds that `send` may overlap with existing methods; it is disabled by default, so a team switches it on only if it wants that rule. Library code that dispatches on objects it does not control is where `__send__` earns its underscores. ## What an interviewer is listening for The key sentence is: `__send__` exists because `send` is a common name that classes override, and because `BasicObject` subclasses have no `Kernel#send` at all. A good answer adds that `__send__` still bypasses visibility, so it is a robustness choice, not a safety one.

  • Is there an underscored version of public_send for generic code?
    No. Ruby has `__send__` on `BasicObject` but no underscored twin of `public_send`, which lives in `Kernel`. Generic code that must respect visibility calls `public_send` and accepts that a class could override it; code on a `BasicObject` proxy cannot call `public_send` on itself at all, because `Kernel` is not included.
  • What happens if a class redefines __send__?
    CRuby allows it but prints a warning, "redefining '__send__' may cause serious problems", and warns similarly on removing or undefining it. The same guard exists for `object_id` and `__id__`, because libraries assume these names reach the interpreter's own implementation.

saying these in an interview costs you the question

  • __send__ respects private methods while send does not
  • __send__ is faster because it skips method lookup
  • BasicObject subclasses can call send because every object has Kernel
  • __send__ is the public-only variant of send
  • on a UDPSocket, send("hi") calls a method named hi