vec_cast() provides general coercions from one type of vector to another, and along with vec_type2() forms the foundation of the vctrs type system. It should generally not be called by R users, but is important for R developers. vec_restore() is designed specifically for casting a bare vector to the original type; it's useful when relying NextMethod() for the actual implementation. vec_cast_common(...) casts a collection to vectors to the same type.

vec_cast(x, to, ...)

vec_cast_common(..., .to = NULL)

vec_restore(x, to, ..., i = NULL)

# S3 method for logical
vec_cast(x, to, ...)

# S3 method for integer
vec_cast(x, to, ...)

# S3 method for double
vec_cast(x, to, ...)

# S3 method for complex
vec_cast(x, to, ...)

# S3 method for raw
vec_cast(x, to, ...)

# S3 method for character
vec_cast(x, to, ...)

# S3 method for list
vec_cast(x, to, ...)

Arguments

x

Vectors to cast.

to, .to

Type to cast to. If NULL, x will be returned as is.

...

For vec_cast_common(), vectors to cast. For vec_cast() and vec_restore(), these dots are only for future extensions and should be empty.

i

The index vector used to slice x when restoration is triggered by vec_slice(). In most cases you don't need this information and can safely ignore that argument.

Value

A vector the same length as x with the same type as to, or an error if the cast is not possible. An error is generated if information is lost when casting between compatible types (i.e. when there is no 1-to-1 mapping for a specific value).

Casting rules

Casting is more flexible than coercion, and allows for the possibility of information loss. This diagram summarises possible coercions. vec_cast() from any type connected to another type, provided that the arrows are followed in only one direction. For example you can cast from logical to character, and list to time, but you can not cast from logical to datetime.

Most casts are not symmetric: you can cast all integers to doubles, but you can only cast a subset of doubles back to integers. If a cast is potentially lossy, an error will be shown whenever an actual loss occurs.

The rules for coercing from a list are fairly strict: each component of the list must be of length 1, and must be coercible to type to. This ensures that a round-trip to and form list is possible, without opening the door to very flexible list flattening (which should be the job of a more specialised function).

S3 dispatch

vec_cast() dispatches on both arguments because casting depends on both the type of x and of to. This is implemented by having methods of vec_cast(), e.g. vec_cast.integer() also be S3 generics, which call e.g. vec_cast.integer.double().

Note that vec_cast() dispatches on its second argument, so that the name of the final method uses the same convention as as.xyz() methods, i.e. vec_cast.integer.double() casts double to integers, in the same way that as.integer.double() would.

Whenever you implement a vec_cast.new_class() generic/method, make sure to always provide vec_cast.new_class.default() and call vec_default_cast() from that method.

See vignette("s3-vector") for full details.

Restoring attributes

A restore is a specialised type of cast, primarily used in conjunction with NextMethod() or a C-level function that works on the underlying data structure. A vec_restore() method can make the following assumptions about x:

  • It has the correct type.

  • It has the correct names.

  • It has the correct dim and dimnames attributes.

  • It is unclassed. This way you can call vctrs generics with x without triggering an infinite loop of restoration.

The length may be different (for example after vec_slice() has been called), and all other attributes may have been lost. The method should restore all attributes so that after restoration, vec_restore(vec_data(x), x) yields x.

To understand the difference between vec_cast() and vec_restore() think about factors: it doesn't make sense to cast an integer to a factor, but if NextMethod() or another low-level function has stripped attributes, you still need to be able to restore them.

The default method copies across all attributes so you only need to provide your own method if your attributes require special care (i.e. they are dependent on the data in some way). When implementing your own method, bear in mind that many R users add attributes to track additional metadata that is important to them, so you should preserve any attributes that don't require special handling for your class.

Examples

# x is a double, but no information is lost vec_cast(1, integer())
#> [1] 1
# When information is lost the cast fails try(vec_cast(c(1, 1.5), integer()))
#> Error : Lossy cast from <double> to <integer>. #> Locations: 2 #> Backtrace: #> █ #> 1. ├─base::tryCatch(...) #> 2. │ └─base:::tryCatchList(expr, classes, parentenv, handlers) #> 3. │ ├─base:::tryCatchOne(...) #> 4. │ │ └─base:::doTryCatch(return(expr), name, parentenv, handler) #> 5. │ └─base:::tryCatchList(expr, names[-nh], parentenv, handlers[-nh]) #> 6. │ └─base:::tryCatchOne(expr, names, parentenv, handlers[[1L]]) #> 7. │ └─base:::doTryCatch(return(expr), name, parentenv, handler) #> 8. ├─base::withCallingHandlers(...) #> 9. ├─base::saveRDS(...) #> 10. ├─base::do.call(...) #> 11. ├─(function (what, args, quote = FALSE, envir = parent.frame()) ... #> 12. ├─(function (..., crayon_enabled, crayon_colors, pkgdown_internet) ... #> 13. │ └─pkgdown::build_site(...) #> 14. │ └─pkgdown:::build_site_local(...) #> 15. │ └─pkgdown::build_reference(...) #> 16. │ └─purrr::map(...) #> 17. │ └─pkgdown:::.f(.x[[i]], ...) #> 18. │ └─pkgdown:::data_reference_topic(...) #> 19. │ ├─pkgdown:::as_data(...) #> 20. │ └─pkgdown:::as_data.tag_examples(...) #> 21. │ └─purrr::pmap_chr(...) #> 22. │ └─pkgdown:::.f(...) #> 23. │ ├─withr::with_options(...) #> 24. │ │ └─base::force(code) #> 25. │ └─evaluate::evaluate(code, env, new_device = TRUE) #> 26. │ └─evaluate:::evaluate_call(...) #> 27. │ ├─evaluate:::timing_fn(...) #> 28. │ ├─evaluate:::handle(...) #> 29. │ │ └─base::try(f, silent = TRUE) #> 30. │ │ └─base::tryCatch(...) #> 31. │ │ └─base:::tryCatchList(expr, classes, parentenv, handlers) #> 32. │ │ └─base:::tryCatchOne(expr, names, parentenv, handlers[[1L]]) #> 33. │ │ └─base:::doTryCatch(return(expr), name, parentenv, handler) #> 34. │ ├─base::withCallingHandlers(...) #> 35. │ ├─base::withVisible(eval(expr, envir, enclos)) #> 36. │ └─base::eval(expr, envir, enclos) #> 37. │ └─base::eval(expr, envir, enclos) #> 38. │ ├─base::try(vec_cast(c(1, 1.5), integer())) #> 39. │ │ └─base::tryCatch(...) #> 40. │ │ └─base:::tryCatchList(expr, classes, parentenv, handlers) #> 41. │ │ └─base:::tryCatchOne(expr, names, parentenv, handlers[[1L]]) #> 42. │ │ └─base:::doTryCatch(return(expr), name, parentenv, handler) #> 43. │ └─vctrs::vec_cast(c(1, 1.5), integer()) #> 44. ├─vctrs:::vec_cast_dispatch(x = x, to = to) #> 45. ├─vctrs::vec_cast.integer(x = x, to = to) /home/travis/build/r-lib/vctrs/R/cast.R:124:2 #> 46. └─vctrs:::vec_cast.integer.double(x = x, to = to) /home/travis/build/r-lib/vctrs/R/type-bare.R:57:2 #> 47. └─vctrs::maybe_lossy_cast(out, x, to, lossy) /home/travis/build/r-lib/vctrs/R/type-bare.R:76:2 #> 48. ├─base::withRestarts(...) /home/travis/build/r-lib/vctrs/R/conditions.R:220:2 #> 49. │ └─base:::withOneRestart(expr, restarts[[1L]]) #> 50. │ └─base:::doWithOneRestart(return(expr), restart) #> 51. └─vctrs:::stop_lossy_cast(...) #> 52. └─vctrs:::stop_vctrs(...) /home/travis/build/r-lib/vctrs/R/conditions.R:249:2
try(vec_cast(c(1, 2), logical()))
#> Error : Lossy cast from <double> to <logical>. #> Locations: 2 #> Backtrace: #> █ #> 1. ├─base::tryCatch(...) #> 2. │ └─base:::tryCatchList(expr, classes, parentenv, handlers) #> 3. │ ├─base:::tryCatchOne(...) #> 4. │ │ └─base:::doTryCatch(return(expr), name, parentenv, handler) #> 5. │ └─base:::tryCatchList(expr, names[-nh], parentenv, handlers[-nh]) #> 6. │ └─base:::tryCatchOne(expr, names, parentenv, handlers[[1L]]) #> 7. │ └─base:::doTryCatch(return(expr), name, parentenv, handler) #> 8. ├─base::withCallingHandlers(...) #> 9. ├─base::saveRDS(...) #> 10. ├─base::do.call(...) #> 11. ├─(function (what, args, quote = FALSE, envir = parent.frame()) ... #> 12. ├─(function (..., crayon_enabled, crayon_colors, pkgdown_internet) ... #> 13. │ └─pkgdown::build_site(...) #> 14. │ └─pkgdown:::build_site_local(...) #> 15. │ └─pkgdown::build_reference(...) #> 16. │ └─purrr::map(...) #> 17. │ └─pkgdown:::.f(.x[[i]], ...) #> 18. │ └─pkgdown:::data_reference_topic(...) #> 19. │ ├─pkgdown:::as_data(...) #> 20. │ └─pkgdown:::as_data.tag_examples(...) #> 21. │ └─purrr::pmap_chr(...) #> 22. │ └─pkgdown:::.f(...) #> 23. │ ├─withr::with_options(...) #> 24. │ │ └─base::force(code) #> 25. │ └─evaluate::evaluate(code, env, new_device = TRUE) #> 26. │ └─evaluate:::evaluate_call(...) #> 27. │ ├─evaluate:::timing_fn(...) #> 28. │ ├─evaluate:::handle(...) #> 29. │ │ └─base::try(f, silent = TRUE) #> 30. │ │ └─base::tryCatch(...) #> 31. │ │ └─base:::tryCatchList(expr, classes, parentenv, handlers) #> 32. │ │ └─base:::tryCatchOne(expr, names, parentenv, handlers[[1L]]) #> 33. │ │ └─base:::doTryCatch(return(expr), name, parentenv, handler) #> 34. │ ├─base::withCallingHandlers(...) #> 35. │ ├─base::withVisible(eval(expr, envir, enclos)) #> 36. │ └─base::eval(expr, envir, enclos) #> 37. │ └─base::eval(expr, envir, enclos) #> 38. │ ├─base::try(vec_cast(c(1, 2), logical())) #> 39. │ │ └─base::tryCatch(...) #> 40. │ │ └─base:::tryCatchList(expr, classes, parentenv, handlers) #> 41. │ │ └─base:::tryCatchOne(expr, names, parentenv, handlers[[1L]]) #> 42. │ │ └─base:::doTryCatch(return(expr), name, parentenv, handler) #> 43. │ └─vctrs::vec_cast(c(1, 2), logical()) #> 44. ├─vctrs:::vec_cast_dispatch(x = x, to = to) #> 45. ├─vctrs::vec_cast.logical(x = x, to = to) /home/travis/build/r-lib/vctrs/R/cast.R:124:2 #> 46. └─vctrs:::vec_cast.logical.double(x = x, to = to) /home/travis/build/r-lib/vctrs/R/type-bare.R:11:20 #> 47. └─vctrs::maybe_lossy_cast(out, x, to, lossy) /home/travis/build/r-lib/vctrs/R/type-bare.R:31:2 #> 48. ├─base::withRestarts(...) /home/travis/build/r-lib/vctrs/R/conditions.R:220:2 #> 49. │ └─base:::withOneRestart(expr, restarts[[1L]]) #> 50. │ └─base:::doWithOneRestart(return(expr), restart) #> 51. └─vctrs:::stop_lossy_cast(...) #> 52. └─vctrs:::stop_vctrs(...) /home/travis/build/r-lib/vctrs/R/conditions.R:249:2
# You can suppress this error and get the partial results allow_lossy_cast(vec_cast(c(1, 1.5), integer()))
#> [1] 1 1
allow_lossy_cast(vec_cast(c(1, 2), logical()))
#> [1] TRUE TRUE
# By default this suppress all lossy cast errors without # distinction, but you can be specific about what cast is allowed # by supplying prototypes allow_lossy_cast(vec_cast(c(1, 1.5), integer()), to_ptype = integer())
#> [1] 1 1
try(allow_lossy_cast(vec_cast(c(1, 2), logical()), to_ptype = integer()))
#> Error : Lossy cast from <double> to <logical>. #> Locations: 2 #> Backtrace: #> █ #> 1. ├─base::tryCatch(...) #> 2. │ └─base:::tryCatchList(expr, classes, parentenv, handlers) #> 3. │ ├─base:::tryCatchOne(...) #> 4. │ │ └─base:::doTryCatch(return(expr), name, parentenv, handler) #> 5. │ └─base:::tryCatchList(expr, names[-nh], parentenv, handlers[-nh]) #> 6. │ └─base:::tryCatchOne(expr, names, parentenv, handlers[[1L]]) #> 7. │ └─base:::doTryCatch(return(expr), name, parentenv, handler) #> 8. ├─base::withCallingHandlers(...) #> 9. ├─base::saveRDS(...) #> 10. ├─base::do.call(...) #> 11. ├─(function (what, args, quote = FALSE, envir = parent.frame()) ... #> 12. ├─(function (..., crayon_enabled, crayon_colors, pkgdown_internet) ... #> 13. │ └─pkgdown::build_site(...) #> 14. │ └─pkgdown:::build_site_local(...) #> 15. │ └─pkgdown::build_reference(...) #> 16. │ └─purrr::map(...) #> 17. │ └─pkgdown:::.f(.x[[i]], ...) #> 18. │ └─pkgdown:::data_reference_topic(...) #> 19. │ ├─pkgdown:::as_data(...) #> 20. │ └─pkgdown:::as_data.tag_examples(...) #> 21. │ └─purrr::pmap_chr(...) #> 22. │ └─pkgdown:::.f(...) #> 23. │ ├─withr::with_options(...) #> 24. │ │ └─base::force(code) #> 25. │ └─evaluate::evaluate(code, env, new_device = TRUE) #> 26. │ └─evaluate:::evaluate_call(...) #> 27. │ ├─evaluate:::timing_fn(...) #> 28. │ ├─evaluate:::handle(...) #> 29. │ │ └─base::try(f, silent = TRUE) #> 30. │ │ └─base::tryCatch(...) #> 31. │ │ └─base:::tryCatchList(expr, classes, parentenv, handlers) #> 32. │ │ └─base:::tryCatchOne(expr, names, parentenv, handlers[[1L]]) #> 33. │ │ └─base:::doTryCatch(return(expr), name, parentenv, handler) #> 34. │ ├─base::withCallingHandlers(...) #> 35. │ ├─base::withVisible(eval(expr, envir, enclos)) #> 36. │ └─base::eval(expr, envir, enclos) #> 37. │ └─base::eval(expr, envir, enclos) #> 38. │ ├─base::try(allow_lossy_cast(vec_cast(c(1, 2), logical()), to_ptype = integer())) #> 39. │ │ └─base::tryCatch(...) #> 40. │ │ └─base:::tryCatchList(expr, classes, parentenv, handlers) #> 41. │ │ └─base:::tryCatchOne(expr, names, parentenv, handlers[[1L]]) #> 42. │ │ └─base:::doTryCatch(return(expr), name, parentenv, handler) #> 43. │ ├─vctrs::allow_lossy_cast(vec_cast(c(1, 2), logical()), to_ptype = integer()) #> 44. │ │ └─base::withCallingHandlers(...) /home/travis/build/r-lib/vctrs/R/conditions.R:266:2 #> 45. │ └─vctrs::vec_cast(c(1, 2), logical()) #> 46. ├─vctrs:::vec_cast_dispatch(x = x, to = to) #> 47. ├─vctrs::vec_cast.logical(x = x, to = to) /home/travis/build/r-lib/vctrs/R/cast.R:124:2 #> 48. └─vctrs:::vec_cast.logical.double(x = x, to = to) /home/travis/build/r-lib/vctrs/R/type-bare.R:11:20 #> 49. └─vctrs::maybe_lossy_cast(out, x, to, lossy) /home/travis/build/r-lib/vctrs/R/type-bare.R:31:2 #> 50. ├─base::withRestarts(...) /home/travis/build/r-lib/vctrs/R/conditions.R:220:2 #> 51. │ └─base:::withOneRestart(expr, restarts[[1L]]) #> 52. │ └─base:::doWithOneRestart(return(expr), restart) #> 53. └─vctrs:::stop_lossy_cast(...) #> 54. └─vctrs:::stop_vctrs(...) /home/travis/build/r-lib/vctrs/R/conditions.R:249:2
# No sensible coercion is possible so an error is generated try(vec_cast(1.5, factor("a")))
#> Error : Can't cast <double> to <factor<127a2>> #> Backtrace: #> █ #> 1. ├─base::tryCatch(...) #> 2. │ └─base:::tryCatchList(expr, classes, parentenv, handlers) #> 3. │ ├─base:::tryCatchOne(...) #> 4. │ │ └─base:::doTryCatch(return(expr), name, parentenv, handler) #> 5. │ └─base:::tryCatchList(expr, names[-nh], parentenv, handlers[-nh]) #> 6. │ └─base:::tryCatchOne(expr, names, parentenv, handlers[[1L]]) #> 7. │ └─base:::doTryCatch(return(expr), name, parentenv, handler) #> 8. ├─base::withCallingHandlers(...) #> 9. ├─base::saveRDS(...) #> 10. ├─base::do.call(...) #> 11. ├─(function (what, args, quote = FALSE, envir = parent.frame()) ... #> 12. ├─(function (..., crayon_enabled, crayon_colors, pkgdown_internet) ... #> 13. │ └─pkgdown::build_site(...) #> 14. │ └─pkgdown:::build_site_local(...) #> 15. │ └─pkgdown::build_reference(...) #> 16. │ └─purrr::map(...) #> 17. │ └─pkgdown:::.f(.x[[i]], ...) #> 18. │ └─pkgdown:::data_reference_topic(...) #> 19. │ ├─pkgdown:::as_data(...) #> 20. │ └─pkgdown:::as_data.tag_examples(...) #> 21. │ └─purrr::pmap_chr(...) #> 22. │ └─pkgdown:::.f(...) #> 23. │ ├─withr::with_options(...) #> 24. │ │ └─base::force(code) #> 25. │ └─evaluate::evaluate(code, env, new_device = TRUE) #> 26. │ └─evaluate:::evaluate_call(...) #> 27. │ ├─evaluate:::timing_fn(...) #> 28. │ ├─evaluate:::handle(...) #> 29. │ │ └─base::try(f, silent = TRUE) #> 30. │ │ └─base::tryCatch(...) #> 31. │ │ └─base:::tryCatchList(expr, classes, parentenv, handlers) #> 32. │ │ └─base:::tryCatchOne(expr, names, parentenv, handlers[[1L]]) #> 33. │ │ └─base:::doTryCatch(return(expr), name, parentenv, handler) #> 34. │ ├─base::withCallingHandlers(...) #> 35. │ ├─base::withVisible(eval(expr, envir, enclos)) #> 36. │ └─base::eval(expr, envir, enclos) #> 37. │ └─base::eval(expr, envir, enclos) #> 38. │ ├─base::try(vec_cast(1.5, factor("a"))) #> 39. │ │ └─base::tryCatch(...) #> 40. │ │ └─base:::tryCatchList(expr, classes, parentenv, handlers) #> 41. │ │ └─base:::tryCatchOne(expr, names, parentenv, handlers[[1L]]) #> 42. │ │ └─base:::doTryCatch(return(expr), name, parentenv, handler) #> 43. │ └─vctrs::vec_cast(1.5, factor("a")) #> 44. ├─vctrs:::vec_cast_dispatch(x = x, to = to) #> 45. ├─vctrs::vec_cast.factor(x = x, to = to) /home/travis/build/r-lib/vctrs/R/cast.R:124:2 #> 46. └─vctrs:::vec_cast.factor.default(x = x, to = to) /home/travis/build/r-lib/vctrs/R/type-factor.R:118:2 #> 47. └─vctrs::vec_default_cast(x, to) /home/travis/build/r-lib/vctrs/R/type-factor.R:146:2 #> 48. └─vctrs::stop_incompatible_cast(x, to) /home/travis/build/r-lib/vctrs/R/cast.R:168:4 #> 49. └─vctrs:::stop_incompatible(...) /home/travis/build/r-lib/vctrs/R/conditions.R:112:2 #> 50. └─vctrs:::stop_vctrs(...) /home/travis/build/r-lib/vctrs/R/conditions.R:48:2
# Cast to common type vec_cast_common(factor("a"), factor(c("a", "b")))
#> [[1]] #> [1] a #> Levels: a b #> #> [[2]] #> [1] a b #> Levels: a b #>
vec_cast_common(factor("a"), Sys.Date(), .to = list())
#> [[1]] #> [[1]][[1]] #> [1] a #> Levels: a #> #> #> [[2]] #> [[2]][[1]] #> [1] "2019-06-18" #> #>