Restart points are named functions that are established with with_restarts(). Once established, you can interrupt the normal execution of R code, jump to the restart, and resume execution from there. Each restart is established along with a restart function that is executed after the jump and that provides a return value from the establishing point (i.e., a return value for with_restarts()).

with_restarts(.expr, ...)



An expression to execute with new restarts established on the stack. This argument is passed by expression and supports unquoting. It is evaluated in a context where restarts are established.


<dynamic> Named restart functions. The name is taken as the restart name and the function is executed after the jump.


Restarts are not the only way of jumping to a previous call frame (see return_from() or return_to()). However, they have the advantage of being callable by name once established.

Life cycle

All the restart functions are in the questioning stage. It is not clear yet whether we want to recommend restarts as a style of programming in R.

See also

return_from() and return_to() for a more flexible way of performing a non-local jump to an arbitrary call frame.


# Restarts are not the only way to jump to a previous frame, but # they have the advantage of being callable by name: fn <- function() with_restarts(g(), my_restart = function() "returned") g <- function() h() h <- function() { rst_jump("my_restart"); "not returned" } fn()
#> [1] "returned"
# Whereas a non-local return requires to manually pass the calling # frame to the return function: fn <- function() g(current_env()) g <- function(env) h(env) h <- function(env) { return_from(env, "returned"); "not returned" } fn()
#> [1] "returned"
# rst_maybe_jump() checks that a restart exists before trying to jump: fn <- function() { g() cat("will this be called?\n") } g <- function() { rst_maybe_jump("my_restart") cat("will this be called?\n") } # Here no restart are on the stack: fn()
#> will this be called? #> will this be called?
# If a restart point called `my_restart` was established on the # stack before calling fn(), the control flow will jump there: rst <- function() { cat("restarting...\n") "return value" } with_restarts(fn(), my_restart = rst)
#> restarting...
#> [1] "return value"
# Restarts are particularly useful to provide alternative default # values when the normal output cannot be computed: fn <- function(valid_input) { if (valid_input) { return("normal value") } # We decide to return the empty string "" as default value. An # altenative strategy would be to signal an error. In any case, # we want to provide a way for the caller to get a different # output. For this purpose, we provide two restart functions that # returns alternative defaults: restarts <- list( rst_empty_chr = function() character(0), rst_null = function() NULL ) with_restarts(splice(restarts), .expr = { # Signal a typed condition to let the caller know that we are # about to return an empty string as default value: cnd_signal("default_empty_string") # If no jump to with_restarts, return default value: "" }) } # Normal value for valid input: fn(TRUE)
#> [1] "normal value"
# Default value for bad input: fn(FALSE)
#> Warning: Creating a condition with `cnd_signal()` is deprecated as of rlang 0.3.0. #> Please use `signal()` instead. #> This warning is displayed once per session.
#> [1] ""
# Change the default value if you need an empty character vector by # defining a calling handler that jumps to the restart. It has to # be calling because exiting handlers jump to the place where they # are established before being executed, and the restart is not # defined anymore at that point: rst_handler <- calling(function(c) rst_jump("rst_empty_chr")) with_handlers(fn(FALSE), default_empty_string = rst_handler)
#> character(0)
# You can use restarting() to create restarting handlers easily: with_handlers(fn(FALSE), default_empty_string = restarting("rst_null"))