skip to content

In Ruby, how do Kernel#autoload and Module#autoload defer loading, and what does autoload? report?

level: middleimportance: should knowfreq 30%

answer

  1. constant name plus a path
  2. require on first reference
  3. registered in a namespace
  4. autoload? returns the path or nil
  5. file must define the constant

basics

~20 s

autoload(:Name, "path") registers a constant whose first reference calls require on the path. Module#autoload registers it inside that module; Kernel#autoload in the current namespace. autoload? returns the pending path, or nil once loaded or never registered.

solid answer

~40 s

`Module#autoload(:Page, "sitegen/page")` records that the constant `Page` in that module is provided by a file; nothing is loaded until code first references `Sitegen::Page`, at which point Ruby calls `require "sitegen/page"` and then returns the constant. `Kernel#autoload` does the same for the class or module whose body you are in, or `Object` at the top level. The path goes through normal `require`, so `$LOAD_PATH`, the once-only rule and `LoadError` all apply. `autoload?(:Page)` returns the registered path while the constant is still pending and `nil` otherwise. If the file loads but does not define the constant, the reference raises `NameError`. Registering a constant that is already defined does nothing.

code

ruby · 10 lines
ruby
module Sitegen
  autoload :Feed, "sitegen/feed"
end

# sitegen/feed.rb forgot the namespace:
#   class Feed; end          # defines ::Feed, not Sitegen::Feed

Sitegen::Feed
# NameError: uninitialized constant Sitegen::Feed
# (with -w: warning that sitegen/feed was expected to define Sitegen::Feed)

go deeper

for a junior

Recall that autoload maps a constant name to a file and loads it the first time the constant is used.

for a middle

Explain the require on first reference, the namespace a Kernel or Module call registers in, what autoload? returns, and the NameError when the file does not define the constant.

for a senior

Weigh startup savings against errors moving to first use and loading in arbitrary threads, and eager-load in tests and before forking or threading.

for a principal

Decide whether a library should autoload at all: it helps large optional surfaces but hides load order, so pair it with a documented eager-load entry point.

## What autoload registers An **autoload** is a promise: "the constant `X` in this namespace is defined by this file". Registering it costs almost nothing; the file is only loaded when the constant is first needed. ```ruby # lib/sitegen.rb module Sitegen autoload :Page, "sitegen/page" autoload :Builder, "sitegen/builder" autoload :Markdown, "sitegen/markdown" end Sitegen::Page # first reference: require "sitegen/page", then return Page Sitegen.autoload?(:Builder) # => "sitegen/builder" (still pending) Sitegen.autoload?(:Page) # => nil (already loaded) ``` | Method | Registers the constant in | Typical use | |---|---|---| | `Module#autoload(name, path)` | the receiver module | `Sitegen.autoload(:Page, "sitegen/page")` from outside | | `Kernel#autoload(name, path)` | the class or module whose body is executing (`Object` at top level) | `autoload :Page, "sitegen/page"` inside `module Sitegen` | | `Module#autoload?(name, inherit = true)` | lookup only | returns the pending path or `nil`; `inherit = false` skips ancestors | The name may be a `Symbol` or a `String`. ## What happens on first reference 1. Code evaluates `Sitegen::Page` (or `Page` inside `Sitegen`, or `Sitegen.const_get(:Page)`). 2. Ruby sees the constant is registered as an autoload and calls `require` with the path. 3. `require` searches `$LOAD_PATH`, runs the file once and records it in `$LOADED_FEATURES`. 4. If the file defined `Sitegen::Page`, the reference returns it; later references are ordinary constant lookups. 5. If the file did not define it, the autoload entry is removed and the reference raises `NameError`. With `-w`, Ruby first warns that the file was expected to define the constant but did not. If the file cannot be found, the reference raises `LoadError`, which surprises code that only expected a `NameError`. ## Rules worth knowing - **Registering over an existing constant does nothing.** If `Page` is already defined normally, `autoload(:Page, ...)` is ignored. Registering the same constant twice replaces the path. - **The path is a `require` path.** Use load-path-relative names (`"sitegen/page"`) or absolute paths built with `File.expand_path("sitegen/page", __dir__)`; a `./` path depends on the working directory like any `require`. - **One file, one constant.** The file named for `Page` should define `Sitegen::Page` exactly, in the same namespace; defining `::Page` at the top level does not satisfy it. - **Files currently being loaded must not be registered for autoload**, as the method documentation warns. ## Why use it at all - **Startup time.** A command-line tool with many subcommands registers them all but loads only the one used. - **Optional dependencies.** A constant whose file needs a heavy or optional library costs nothing unless someone uses it. - **Ordering.** Files no longer need to be required in dependency order; the first reference pulls each one in. The trade-offs are that errors move from boot time to first use, and loading happens in whatever thread touches the constant first. ## How it relates to the other loaders - `require` loads now; `autoload` schedules a `require` for later. - `load` is never used by autoload, so an autoloaded file runs at most once per process. - Framework autoloaders that map file names to constants automatically build on this same core mechanism, with their own conventions on top. ## Inspecting autoload state - `Module#autoload?(name)` answers the pending path or `nil`; `autoload?(name, false)` ignores autoloads registered on ancestors, so a subclass can tell whether the registration is its own. - `Module#constants` lists pending autoload names too, without loading them, which makes a simple eager-load loop possible: `constants.each { |c| const_get(c) }`. - Referencing the constant, or calling `const_get`, is what triggers the load. ```ruby class Theme autoload :Palette, "sitegen/theme/palette" end class DarkTheme < Theme; end DarkTheme.autoload?(:Palette) # => "sitegen/theme/palette" (found on Theme) DarkTheme.autoload?(:Palette, false) # => nil (not registered on DarkTheme) ```

  • What does `autoload?` return after the constant has been loaded?
    `nil`. It reports only pending autoloads, returning the registered path while the constant has not been loaded yet. Once the first reference has loaded the file, the entry is gone and the constant is an ordinary one, so `autoload?` answers `nil` just as for a constant that was never registered.
  • Why can an autoloaded constant raise `LoadError` at an unexpected place?
    The `require` happens at the first reference, wherever that is. If the path is wrong or the load path differs in that process, the failure surfaces deep inside business code instead of at boot. Eager-loading in tests or at startup, by referencing each constant or requiring the files, moves the error back to a predictable place.

An autoload is a library hold slip: the shelf keeps a card saying which book will fill it, and the book is fetched only when the first reader asks for that title.

saying these in an interview costs you the question

  • autoload loads the file immediately when it is registered
  • autoload uses load, so the file runs on every reference
  • autoload? returns true or false
  • Registering autoload over an existing constant replaces it
  • A file that defines the constant at the top level satisfies a namespaced autoload