I just realized that you were changing the name of the column in the select() function. I have to admit that I would never do this in select() because that is why you have mutate() and rename(). However, I would only do this if I was creating a final table and no more changes to the data were taking place but the users needed different column names. Nice to know that this can be done.
Since you brought up the .data pronoun, do you know the difference between .data and .? The above could have been written as:
mtcars %>% select(mileage = .[[var_col_name]])
.data takes the data.frame from the previous pipe, where as . takes the original data set that begins the pipe. This matters if you have several filters, mutations, and summaries as you pipe your data along. Just an FYI if you see the . in code.
This understanding is incorrect. . refers to the object on the LHS of the "current stage" of the pipe. It does not refer to the original data set at the start of the pipeline. You can verify this with the following code.
library(dplyr, warn.conflicts = FALSE)
#> Warning: package 'dplyr' was built under R version 4.0.4
# If . referred to the original data.frame, this code should work. But it does not.
iris %>%
select(-Species) %>%
rename(Species_new = .[[Species]])
#> Error: object 'Species' not found
.data is a pronoun; a special construct which is used to disambiguate between data variables and environment variables. See this post for a detailed write-up on this topic.
I think you are confusing how tidyverse selects variables with non-standard evaluation and the how base R's subsetting operator []. This operator takes an index or a character value, the column name. Please refer to AdvancedR for a much more thorough explanation.
Your example fails because the operator searches the global environment for an object names Species, not in the dataframe that is passed along. Using your example, lets see what happens when you supply the argument that it expects, and the issue of the .:
When you pass a character to the [] operator using the ., it does what you would "not" expect since you de-selected the Species column. That is because it references the original data frame.
When you pass a character to the [] operator using the .data, it does what you "would" expect since you de-selected the Species column. That is because it references the piped data frame.
> iris %>%
+ select(-Species) %>%
+ rename(Species_new = .data[["Species"]]) %>% head()
Error: Column `Species` not found in `.data`
Run `rlang::last_error()` to see where the error occurred.
If we add an object named Species <- "Species", your code would then work even though you de-selected it in the pipeline. That can be quite dangerous if some of you objects have the same name as your column names.
I hope this helps. I know when I first started using R, I thought the same, that .data and . where the same thing. But then I started getting results that I did not expect and with further research and experience I discovered the above.