Vim: What's the difference between let and set?

What's the difference between let and set in the vim editor?

I've always wondered why both of them exist?

Also, I'd be interested to hear its historical background.


Solution 1:

:set is for setting options, :let for assigning a value to a variable.

It happens that the value for an option is linked to the name of the option prepended by a & (the &option-name construct then behaves very similar to "ordinary" variables). So, the following are equivalent:

:set  tw=40
:let &tw=40

But, for example, assigning 50 to the global variable foo (:let g:foo=50) cannot be achieved with a :set command (because g:foo is a variable and not an option).

Some options are boolean like. When setting these, no value is needed (as in :set noic and the opposite :set ic).

Solution 2:

Set is a more user-friendly interface specialized for options

E.g.

:verbose set

to display all options in effect.

:set tw=40

Will work as a shorthand for set textwidth=40

:set wrap&

Will set the default value for option wrap

:set nowrap

Will unset the option

:set wrap!

Will toggle the option

Most importantly,

:setTab # to get tab completion!

Few of the above can (easily) be achieved with let.

Solution 3:

:set only works with options, and sehe's answer showcases some good usage examples.

:let on the other hand can do almost everything that :set can do, plus more. It can assign a value to

  • a variable, e.g. let vi = 'vim'
  • an option, e.g. let &tw = 40
  • a register, e.g. let @a = $HOME . '/vimfiles'
  • an environment variable, e.g. let $NOTHING = 'NOTHING'

Another major difference is that the right hand side of :let is an expression, meaning you can do things like string concatenation (as seen in my register example above) and arithmetic operations (e.g. let &tw = 40 + 60). This also means that you have to quote the value if it's a string. :set on the other hand reads the value verbatim.

It's easier to use :set with options even though :let can also do most of it, Here are some comparison using sehe's examples ("n/a" means no way to do it with :let)

  • :verbose set vs n/a (don't think there's another way to list all options)
  • :set tw=40 vs :let &tw = 40 (yes, you can use the same shorthand in let too)
  • :set wrap& vs n/a
  • :set nowrap vs :let &wrap = 0 (for boolean options, 0 is false and 1 is true)
  • :set wrap! vs :let &wrap = !&wrap

A few more examples

  • print the value of an option: :set formatoptions? vs :echo &formatoptions (let doesn't print values, unlike set)
  • assigning to multiple options at the same time:

    :set et sw=4 sts=4 
    

    vs

    :let [&et, &sw, &sts] = [0, 4, 4]
    
  • set global option: setglobal et vs let &g:et = 1

  • set local option: setlocal et vs let &l:et = 1

See :h :set and :h :let for more details


tl;dr

:set only works with options but the syntax is much simpler. :let works with not just options but also variables, registers, and environment variables. Unlike :set, the right hand side of :let is an expression.

Solution 4:

Expanding on what people have written about :let, I've noticed that it can be used to assign a value in a variable to an option, something :set can't do. For example, this function uses let to assign the value in the global variable orig_tw to the textwidthoption:

" Toggle Autowrap
" Default of 72 but can be overridden by tw settings in other vimrc files
let g:orig_tw = 72
function Toggle_autowrap_mode()
    if &textwidth == 0
        " Must use let instead of set here in order for g:orig_tw to be
        " evaluated properly
        let &textwidth = g:orig_tw
        echo "Autowrap mode on tw=" . &textwidth
    else
        let g:orig_tw = &textwidth
        set textwidth=0
        echo "Autowrap mode off tw=" . &textwidth
    endif
endfunction

noremap _A :call Toggle_autowrap_mode()<CR>