There is an example with dplyr::select in https://cran.r-project.org/web/packages/rlang/vignettes/tidy-evaluation.html that uses:
dplyr::select(df, !!cols_to_select)
Why? Let's look at the options you mentioned:
Option 1
dplyr::select(df, cols_to_select)
As you say, this fails if cols_to_select is the column name in df, so this is not true.
Option 4
cols_to_select_syms <- rlang::syms(c("b", "d")) dplyr::select(df, !!!cols_to_select_syms)
This looks more confusing than other solutions.
Options 2 and 3
dplyr::select(df, !!cols_to_select) dplyr::select(df, !!!cols_to_select)
These two solutions give the same results in this case. You can see the output !!cols_to_select and !!!cols_to_select by doing:
dput(rlang::`!!`(cols_to_select)) # c("b", "d") dput(rlang::`!!!`(cols_to_select)) # pairlist("b", "d")
Operator !! or UQ() immediately evaluates its argument in its context, and that is what you want.
Operator !!! or UQS() used to pass multiple arguments to a function at the same time.
For character column names, as in your example, it does not matter whether you specify them as one vector of length 2 (using !! ) or as a list with two vectors of length one (using !!! ). For more complex use cases, you will need to use a few list arguments: (using !!! )
a <- quos(contains("c"), dplyr::starts_with("b")) dplyr::select(df, !!a)