Skip to content
Destructuring
step 1/7

Reading — step 1 of 7

Learn

~2 min readSequences, Strings, Destructuring

Destructuring lets you bind names to parts of a data structure in one step. Pervasive in Clojure code. "Clojure for the Brave and True" treats it as everyday syntax.

Vector destructuring

(let [[a b c] [1 2 3]]
  (println a b c))
;; 1 2 3

Positional binding. Skip elements with _:

(let [[_ b _] [1 2 3]]
  b)        ;; 2

Rest with &:

(let [[head & tail] [1 2 3 4 5]]
  [head tail])
;; [1 (2 3 4 5)]

:as for the whole thing:

(let [[a b :as v] [1 2 3]]
  [a b v])
;; [1 2 [1 2 3]]

Map destructuring

The most common pattern in Clojure:

(let [{:keys [name age]} {:name "Ada" :age 36}]
  [name age])
;; ["Ada" 36]

:keys binds variables matching the key names. Cleaner than:

(let [m {:name "Ada" :age 36}
      name (:name m)
      age (:age m)]
  [name age])

With defaults via :or:

(let [{:keys [name age role] :or {role :user}}
      {:name "Ada" :age 36}]
  [name age role])
;; ["Ada" 36 :user]

Renaming with positional binding:

(let [{user-name :name user-age :age} {:name "Ada" :age 36}]
  [user-name user-age])

The key goes on the right of :, the bound name on the left.

Function arguments

Destructuring works in function parameters too:

(defn greet [{:keys [name age]}]
  (str "Hi, " name ", you're " age))

(greet {:name "Ada" :age 36})
;; "Hi, Ada, you're 36"

Variadic args with destructuring:

(defn process [first & rest]
  (println "first:" first)
  (println "rest:" rest))

(process 1 2 3 4)
;; first: 1
;; rest: (2 3 4)

Nested destructuring

(let [{:keys [user]} {:user {:name "Ada" :role :admin}}
      {:keys [name role]} user]
  [name role])

Or inline:

(let [{{:keys [name role]} :user} {:user {:name "Ada" :role :admin}}]
  [name role])

Readable when shallow; gets gnarly when deep. For deeply nested data, consider helper functions or specter (a Clojure library for nested data manipulation).

When to use destructuring

Yes:

  • Binding parts of data with named meaning
  • Function parameters that accept maps of options
  • Iterating over key-value pairs

No:

  • When you only use the whole value once
  • When the structure is dynamic (use get / get-in)
  • When destructuring becomes harder to read than direct access

Common mistakes

  • Forgetting :or for defaults — without, missing keys bind to nil. Often want a default.
  • Mixing positional and map destructuring confusingly — keep the level simple.
  • Deep nesting — readable for 1-2 levels, painful beyond. Use get-in or a library.
  • Renaming syntax confusion{user-name :name} (rename) vs {:keys [name]} (use as-is). Different tools.
  • String keys{:keys [foo]} only works for keyword keys. For strings: {:strs [foo]}. For symbols: {:syms [foo]}.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…