The effect is that each value is evaluated only once, up-front in the binding thus introduced (hence the alternate name once-only). An implementation is:
(defmacro rebinding (variables &body body)
(loop with prefix = (symbol-name '#:re)
for var in variables
for g = (gensym prefix)
for temp = `(gensym ,(string var))
collect `(,g ,temp) into gensyms
collect ``(,,g ,,var) into temps
collect `(,var ,g) into renames
finally (return `(let ,gensyms
`(let (,,.temps)
,(let ,renames
,@body))))))
In use:
Applying macroexpand-1 to this expression produces (in backquote notation):
(LET ((#:RE23691 (GENSYM "X"))
(#:RE23692 (GENSYM "Y"))
(#:RE23693 (GENSYM "Z")))
`(LET ((,#:RE23691 ,X)
(,#:RE23692 ,Y)
(,#:RE23693 ,Z))
,(LET ((X #:RE23691)
(Y #:RE23692)
(Z #:RE23693))
`(QUUX (+ ,X ,Y)
(* ,X ,Z)))))
Propagating values from the outermost LET gives:
`(LET ((#:X23706 ,X)
(#:Y23707 ,Y)
(#:Z23708 ,Z))
,(LET ((X '#:X23706)
(Y '#:Y23707)
(Z '#:Z23708))
`(QUUX (+ ,X ,Y)
(* ,X ,Z))))
Evaluating the inner, comma-fronted LET reduces this to:
`(LET ((#:X23706 ,X)
(#:Y23707 ,Y)
(#:Z23708 ,Z))
(QUUX (+ #:X23706 #:Y23707)
(* #:X23706 #:Z23708)))
In the above output, keep in mind that READing the same gensym twice would produce two different (uninterned) symbols. In the REPL:
? (eq '#:X23706 '#:X23706) NIL
The *print-circle* option controls whether the printer will detect shared components.
An Exercise for the Reader
There's a variant where the body is "spliced" into the expansion. This would allow—intentionally or otherwise—for (say) declarations:
Hint: it's been posted to comp.lang.lisp.