#[derive(IntoList)]
struct Person {
name: String,
age: i32,
}
#[extendr]
fn create_person() -> Person {
Person {
name: String::from("Alice"),
age: 30,
}
}Rust Structs to Lists
When returning structured data from Rust to R, named lists are often the most natural representation. The IntoList derive macro converts Rust structs into R lists where each field becomes a named element.
Basic Usage
Derive IntoList on a struct to convert it into a named R list. Each field is converted to an Robj and assembled into a named list. This approach has some overhead but provides a straightforward way to return structured results. Note that this creates a one-way conversion—there is no built-in method to convert the list back into the Rust struct. For stateful objects that need methods, use #[extendr] on an impl block with external pointers instead. See IntoList vs External Pointers below for additional details.
The field names become element names in the resulting list:
create_person()
#> $name
#> [1] "Alice"
#>
#> $age
#> [1] 30#[derive(IntoList)]
struct Analysis {
id: i32,
values: Vec<f64>,
passed: bool,
}
#[extendr]
fn run_analysis() -> Analysis {
Analysis {
id: 123,
values: vec![1.5, 2.7, 3.9],
passed: true,
}
}run_analysis()
#> $id
#> [1] 123
#>
#> $values
#> [1] 1.5 2.7 3.9
#>
#> $passed
#> [1] TRUEAny type that can be converted into an Robj can be used as a field. If a field type cannot be converted, exclude it using #[into_list(ignore)] or reconsider the approach.
Ignoring Fields
Not all types in Rust can be converted to R. Skip serializing these fields by using #[into_list(ignore)]:
#[derive(IntoList)]
struct Config {
setting: String,
value: i32,
#[into_list(ignore)]
internal_cache: Vec<u8>,
}
#[extendr]
fn get_config() -> Config {
Config {
setting: String::from("timeout"),
value: 30,
internal_cache: vec![1, 2, 3],
}
}Note that the ignored field does not appear in the resultant list:
config <- get_config()
config
#> $setting
#> [1] "timeout"
#>
#> $value
#> [1] 30config$internal_cache
#> NULLIntoList vs External Pointers
IntoList takes Rust structs and serializes them as an R list. The result is just that—an R list. The list does not store any reference to the Rust struct. That is lost.
The #[extendr] macro creates a reference to the Rust struct itself. This can then be used to pass the Rust struct from function to function.
#[extendr]
struct StatefulCounter {
count: i32,
}
#[extendr]
impl StatefulCounter {
fn new() -> Self {
StatefulCounter { count: 0 }
}
fn increment(&mut self) {
self.count += 1;
}
fn get(&self) -> i32 {
self.count
}
}StatefulCounter keeps state in Rust and exposes methods:
counter <- StatefulCounter$new()
counter$increment()
counter$increment()
counter$get()
#> [1] 2