Not a direct answer but you might also be able to use a different way to create the final data.frames, e.g. with purrr or another way to iterate (e.g. lapply and then do.call+rbind)? Having a function create empty data.frames is not conventional (I'm not saying it's necessarily bad, though).
Pseudo-code.
calculate_thing <- function(stuff) {
# this returns a df
blabla(stuff)
}
calculate_thing2 <- function(stuff) {
# this returns a df
blabla2(stuff)
}
# stuff_list is assumed to be a list
df <- purrr::map_df(stuff_list, calculate_thing)
df2 <- purrr::map_df(stuff_list, calculate_thing2)
purrr intro.
If your input data is in a data.frame you might also enjoy dplyr rowwise operations.
This slidedeck by Jenny Bryan might have slightly outdated tidyverse advice given recent changes (e.g. the rowwise vignette linked above is more recent) but this quote is still very valid:
"Of course someone has to write loops. It doesn’t have to be you."