\let\foo\bar vs \let\foo=\bar (let with equals sign)

There is a case where = is mandatory, besides the obvious one when you want to \let a token to be the same as =:


is the only correct form.

The syntax rules for \let are

\let<control sequence><equals><one optional space><token>

where only <equals> requires an explanation: it is

<optional spaces> | <optional spaces>=

where = must have category code 12 (and be explicit). Note that no expansion is attempted during a \let assignment, so the optional spaces must be explicit. The following example should not be taken seriously, but it works:

\expandafter\let\expandafter\cs\four= \relax

would make \cs equivalent to \relax. This is admittedly esoteric.

However there's a place where the = and the following optional space are mandatory rather than optional.

Suppose we want a macro that tests if the first token in its argument is a space and takes some action if it is. In the following example, the space will be removed.


\def\foo#1{\afterassignment\@checkspace\let\next= #1}
    \typeout{Space found and removed}%
    \typeout{Space not found}\next

|\foo{No space}|
|\foo{ A space}|
|\foo{~ isn't a space}|

enter image description here

The terminal output will be

Space not found
Space found and removed
Space not found

If only \let\next is used, a space would indeed be removed because of the syntax rules, but we'd not be able to distinguish if a space was there or not; worse, if the token list begins with = we'd be in big trouble. With \let\next= and no space, a leading = would be honored, but still an initial space would be removed with no notice.

There are other methods, for example with \futurelet, to solve the same problem. One might enjoy looking at how booktabs.sty defines \futurenonspacelet using a trick similar to this one.

Actually Knuth is quite coherent in his usage of the optional = in plain.tex. All top level \let instructions have it, because this can only impact by a few microseconds in the processing of the instruction, while inside definitions the = is rarely used. It appears in the definition of


but in all the other replacement texts it's omitted, because it impacts on memory usage: each token in a replacement text occupies space and, when TeX was developed, memory space was a concern.

Conversely, in manmac.tex all \let instructions have = (except, quite strangely, in the definition of \footnote). The difference is that, in case memory is scarce when typesetting the TeXbook, manmac.tex can be easily edited, while plain.tex is “for everybody” and so must be optimized.

There is one case where you must use =. You write a macro that ends with a \let command, and you want to be able to tell whether a space follows it. In the following we use a trick to make \spacetoken equal to a space (e.g., latex.ltx does this with \@sptoken).

\def:{\let\spacetoken= }\: %

The analysis is that \let\spacetoken= allows one optional space to follow the equal sign, and that is the one in the macro definition, thus \spacetoken equals a space after \: is executed. This can be used to test if a space follows a macro argument:

\def\X#1{\def\temp{#1}\afterassignment\Y\let\next= }
\def\Y{\ifx\next\spacetoken\message{Found a space after \temp!}\fi}
\X{abc} % space following the argument.

In this case, \next can become equal to a space and \Y can test that. The \message produced above is "Found a space after abc!".

No, this is not true in all cases. One particular case is the difference between


(very likely leading to the error: Undefined macro \myrelax) and


(doing what we want).

Otherwise, there should be no difference. I prefer the variant without =. Notice that this is not the only case where you can put = or not. It is similar for all TeX-core-style assignments to counters, dimensions and glues.

There's actually one case that wouldn't work with =, and that is when you want to \activeate = and \let it something:

% output: 123


% error: Undefined control sequence. =
% if you continue by `q`:
% output: 2 13


Tex Core