Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ Cargo.lock
*.pdb
# r specific
.Rproj.user/
*.Rproj
*.Rproj
**/*.quarto_ipynb
15 changes: 15 additions & 0 deletions _freeze/user-guide/default-args/execute-results/html.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"hash": "0e65dcf08a918c5401ff7a49e3a425dc",
"result": {
"engine": "knitr",
"markdown": "---\ntitle: Default function arguments\ndate: \"2025-10-25\"\nfreeze: true\n---\n\n\n\nIn Rust, all function arguments are required—there are no default arguments. However, for R packages, having default arguments is essential for creating ergonomic and user-friendly APIs. The `#[extendr]` macro provides the `default` parameter allowing you to specify default values that will be added to the generated R wrapper functions.\n\n## The `default` attribute\n\nThe `default` attribute is applied to individual function arguments using the syntax `#[extendr(default = \"value\")]`. The value you provide is a string that will be inserted directly into the generated R function signature. This means you can use any valid R expression as a default value.\n\n```rust\n#[extendr]\nfn my_function(#[extendr(default = \"NULL\")] x: Robj) {\n // function body\n}\n```\n\nThis generates an R function with the signature `my_function(x = NULL)`.\n\n## Basic examples\n\nLet's start with a simple function that checks if an argument is `NULL`. By setting the default to `\"NULL\"`, users can call the function without providing any arguments:\n\n\n::: {.cell}\n\n```{.rust .cell-code}\n#[extendr]\nfn check_default(#[extendr(default = \"NULL\")] x: Robj) -> bool {\n x.is_null()\n}\n```\n:::\n\n\nNow you can call this function without any arguments, and it will use the default value:\n\n\n::: {.cell}\n\n```{.r .cell-code}\ncheck_default()\n#> [1] TRUE\n```\n:::\n\n\nOr provide an explicit value:\n\n\n::: {.cell}\n\n```{.r .cell-code}\ncheck_default(42)\n#> [1] FALSE\n```\n:::\n\n\n## Working with logical defaults\n\nDefault arguments work with any R type. Here's an example with a logical default value:\n\n\n::: {.cell}\n\n```{.rust .cell-code}\n#[extendr]\nfn greet(name: &str, #[extendr(default = \"FALSE\")] loud: bool) -> String {\n let greeting = format!(\"Hello, {}\", name);\n if loud {\n greeting.to_uppercase()\n } else {\n greeting\n }\n}\n```\n:::\n\n\n\n::: {.cell}\n\n```{.r .cell-code}\n# Using the default (quiet greeting)\ngreet(\"Alice\")\n#> [1] \"Hello, Alice\"\n\n# Override the default\ngreet(\"Alice\", loud = TRUE)\n#> [1] \"HELLO, ALICE\"\n```\n:::\n\n\n## Multiple defaults\n\nYou can use multiple default arguments in a single function. Just remember that in R, arguments with defaults should typically come after required arguments for best practices:\n\n\n::: {.cell}\n\n```{.rust .cell-code}\n#[extendr]\nfn multiply(\n x: f64,\n #[extendr(default = \"1.0\")] multiplier: f64,\n #[extendr(default = \"FALSE\")] round_result: bool\n) -> f64 {\n let result = x * multiplier;\n if round_result {\n result.round()\n } else {\n result\n }\n}\n```\n:::\n\n\n\n::: {.cell}\n\n```{.r .cell-code}\n# All defaults\nmultiply(5.5)\n#> [1] 5.5\n\n# Custom multiplier\nmultiply(5.5, multiplier = 2.5)\n#> [1] 13.75\n\n# Custom multiplier and rounding\nmultiply(5.5, multiplier = 2.5, round_result = TRUE)\n#> [1] 14\n```\n:::\n\n\n## Important notes\n\n- The value in `default = \"...\"` is inserted directly into the R function signature, so it must be valid R code\n- You can use any R expression, including function calls: `#[extendr(default = \"getOption('my_option')\")]`\n- The default values only affect the R wrapper—your Rust function still receives whatever value the R user provides (or the default if they don't)\n",
"supporting": [],
"filters": [
"rmarkdown/pagebreak.lua"
],
"includes": {},
"engineDependencies": {},
"preserve": {},
"postProcess": true
}
}
1 change: 1 addition & 0 deletions _quarto.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ website:
- section: "Error Handling"
contents:
- user-guide/error-handling/basic-error-handling.qmd
- user-guide/default-args.qmd
- user-guide/serde-integration.qmd
- user-guide/tokio.qmd
- user-guide/cran-publishing.qmd
Expand Down
108 changes: 108 additions & 0 deletions user-guide/default-args.qmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
---
title: Default function arguments
date: "`r Sys.Date()`"
freeze: true
---

```{r, include = FALSE}
library(rextendr)
```

In Rust, all function arguments are required—there are no default arguments. However, for R packages, having default arguments is essential for creating ergonomic and user-friendly APIs. The `#[extendr]` macro provides the `default` parameter allowing you to specify default values that will be added to the generated R wrapper functions.

## The `default` attribute

The `default` attribute is applied to individual function arguments using the syntax `#[extendr(default = "value")]`. The value you provide is a string that will be inserted directly into the generated R function signature. This means you can use any valid R expression as a default value.

```rust
#[extendr]
fn my_function(#[extendr(default = "NULL")] x: Robj) {
// function body
}
```

This generates an R function with the signature `my_function(x = NULL)`.

## Basic examples

Let's start with a simple function that checks if an argument is `NULL`. By setting the default to `"NULL"`, users can call the function without providing any arguments:

```{extendrsrc engine.opts = list(use_dev_extendr=TRUE)}
#[extendr]
fn check_default(#[extendr(default = "NULL")] x: Robj) -> bool {
x.is_null()
}
```

Now you can call this function without any arguments, and it will use the default value:

```{r}
check_default()
```

Or provide an explicit value:

```{r}
check_default(42)
```

## Working with logical defaults

Default arguments work with any R type. Here's an example with a logical default value:

```{extendrsrc engine.opts = list(use_dev_extendr=TRUE)}
#[extendr]
fn greet(name: &str, #[extendr(default = "FALSE")] loud: bool) -> String {
let greeting = format!("Hello, {}", name);
if loud {
greeting.to_uppercase()
} else {
greeting
}
}
```

```{r}
# Using the default (quiet greeting)
greet("Alice")

# Override the default
greet("Alice", loud = TRUE)
```

## Multiple defaults

You can use multiple default arguments in a single function. Just remember that in R, arguments with defaults should typically come after required arguments for best practices:

```{extendrsrc engine.opts = list(use_dev_extendr=TRUE)}
#[extendr]
fn multiply(
x: f64,
#[extendr(default = "1.0")] multiplier: f64,
#[extendr(default = "FALSE")] round_result: bool
) -> f64 {
let result = x * multiplier;
if round_result {
result.round()
} else {
result
}
}
```

```{r}
# All defaults
multiply(5.5)

# Custom multiplier
multiply(5.5, multiplier = 2.5)

# Custom multiplier and rounding
multiply(5.5, multiplier = 2.5, round_result = TRUE)
```

## Important notes

- The value in `default = "..."` is inserted directly into the R function signature, so it must be valid R code
- You can use any R expression, including function calls: `#[extendr(default = "getOption('my_option')")]`
- The default values only affect the R wrapper—your Rust function still receives whatever value the R user provides (or the default if they don't)