Reference page
ALTREP Quick Reference
One-page reference for miniextendr's ALTREP system.
One-page reference for miniextendrβs ALTREP system.
πConversion Methods
πIntoR vs IntoRAltrep
| Method | Conversion | Memory | Speed (1M elements) | Use When |
|---|---|---|---|---|
.into_sexp() | Copy to R | R heap +3.8 MB | 0.44 ms | Small data, R needs DATAPTR |
.into_sexp_altrep() | Zero-copy wrap | R heap +0 MB | 0.20 ms (2.2x faster) | Large data, lazy eval |
Altrep(x) | Same as above | R heap +0 MB | 0.20 ms (2.2x faster) | Explicit wrapper style |
*Measured on Apple M-series, R 4.5
πQuick Decision Guide
Is your data > 1000 elements?
ββ Yes β Use .into_sexp_altrep()
ββ No
ββ Will R modify it?
ββ Yes β Use .into_sexp() (copy)
ββ No β Either works, .into_sexp() is simplerπExamples
// Small data - copy is fine
#[miniextendr]
fn get_config() -> Vec<i32> {
vec![1, 2, 3] // Automatically copies via IntoR
}
// Large data - use ALTREP
#[miniextendr]
fn get_large_data() -> SEXP {
let data = vec![0; 1_000_000];
data.into_sexp_altrep() // Zero-copy!
}
// Lazy computation - definitely ALTREP
#[miniextendr]
fn fibonacci_seq(n: i32) -> SEXP {
fibonacci_iterator(n as usize)
.collect::<Vec<_>>()
.into_sexp_altrep()
}
// Range - already lazy
#[miniextendr]
fn int_range(from: i32, to: i32) -> SEXP {
(from..to).collect::<Vec<_>>().into_sexp_altrep()
}πThe 7 Vector Types
| R Type | Trait | Element Type | Required Method |
|---|---|---|---|
| integer | AltIntegerData | i32 | fn elt(&self, i: usize) -> i32 |
| numeric | AltRealData | f64 | fn elt(&self, i: usize) -> f64 |
| logical | AltLogicalData | Logical | fn elt(&self, i: usize) -> Logical |
| raw | AltRawData | u8 | fn elt(&self, i: usize) -> u8 |
| complex | AltComplexData | Rcomplex | fn elt(&self, i: usize) -> Rcomplex |
| character | AltStringData | Option<&str> | fn elt(&self, i: usize) -> Option<&str> |
| list | AltListData | SEXP | fn elt(&self, i: usize) -> SEXP |
πMinimal Example: Field-Based Derive (1 step)
// Everything generated from the derive β no trait impls needed
#[derive(miniextendr_api::AltrepInteger)]
#[altrep(len = "len", elt = "value", class = "MyClass")]
pub struct MyData { value: i32, len: usize }πMinimal Example: Manual Traits (3 steps)
// 1. Define data struct with registration derive
#[derive(miniextendr_api::Altrep)]
#[altrep(class = "MyClass")]
pub struct MyData { value: i32, len: usize }
// 2. Implement required traits
impl AltrepLen for MyData {
fn len(&self) -> usize { self.len }
}
impl AltIntegerData for MyData {
fn elt(&self, _i: usize) -> i32 { self.value }
}
// 3. Generate low-level traits
miniextendr_api::impl_altinteger_from_data!(MyData);
In both cases, use MyData { value: 42, len: 100 }.into_sexp() to create the ALTREP vector.
πOptional Methods
| Feature | Trait Method | Macro Option | When to Use |
|---|---|---|---|
| Bulk access | get_region() | - | Faster than looping elt() |
| Dataptr | AltrepDataptr<T> | dataptr | Data in contiguous memory |
| Serialization | AltrepSerialize | serialize | Save/load support needed |
| Subsetting | extract_subset() | subset | O(1) subset possible |
| Mutation | set_elt() | set_elt | Mutable String/List |
| NA hint | no_na() | - | Enables optimizations |
| Sorted hint | is_sorted() | - | Enables optimizations |
| Sum | sum() | - | O(1) computation possible |
| Min | min() | - | O(1) computation possible |
| Max | max() | - | O(1) computation possible |
πDerive Syntax
πField-based derive (generates everything)
#[derive(miniextendr_api::AltrepInteger)]
#[altrep(len = "len_field", elt = "value_field", class = "ClassName")]
pub struct MyType { value_field: i32, len_field: usize }
// Optional: dataptr, serialize, subset via #[altrep(...)] options
#[derive(miniextendr_api::AltrepInteger)]
#[altrep(len = "len", elt_delegate = "data", dataptr, class = "DelegateType")]
pub struct MyType2 { data: Vec<i32>, len: usize }πManual traits macro syntax
// Basic (elt only)
impl_altinteger_from_data!(MyType);
// With dataptr
impl_altinteger_from_data!(MyType, dataptr);
// With serialization
impl_altinteger_from_data!(MyType, serialize);
// Multiple options
impl_altinteger_from_data!(MyType, dataptr, serialize, subset, set_elt);πAvailable Macros
impl_altinteger_from_data!()- Integer vectorsimpl_altreal_from_data!()- Real (numeric) vectorsimpl_altlogical_from_data!()- Logical vectorsimpl_altraw_from_data!()- Raw (byte) vectorsimpl_altcomplex_from_data!()- Complex vectorsimpl_altstring_from_data!()- String (character) vectorsimpl_altlist_from_data!()- List vectors
πSortedness Values
use miniextendr_api::altrep_data::Sortedness;
fn is_sorted(&self) -> Option<Sortedness> {
Some(Sortedness::Increasing) // 1, 2, 3, ...
Some(Sortedness::Decreasing) // 3, 2, 1, ...
Some(Sortedness::IncreasingNAsFirst) // NA, 1, 2, 3
Some(Sortedness::DecreasingNAsFirst) // NA, 3, 2, 1
Some(Sortedness::Unsorted) // No pattern
None // Unknown (R will check if needed)
}πLogical Values
use miniextendr_api::altrep_data::Logical;
impl AltLogicalData for MyData {
fn elt(&self, i: usize) -> Logical {
Logical::True // TRUE (1)
Logical::False // FALSE (0)
Logical::Na // NA
}
}πCommon Patterns
πConstant Vector
fn elt(&self, _i: usize) -> T { self.constant_value }πArithmetic Sequence
fn elt(&self, i: usize) -> T { self.start + i * self.step }πExternal Data
fn elt(&self, i: usize) -> T { self.source[self.offset + i] }πComputed
fn elt(&self, i: usize) -> T { (self.function)(i) }πDATAPTR Decision Tree
Do you have data in memory?
ββ Yes (Vec/Box/slice)
β ββ Provide dataptr() β Fast operations
β
ββ No (computed/lazy)
ββ Will users need DATAPTR often?
ββ Yes β Implement with cache
ββ No β Skip dataptr()πDebugging Checklist
-
Function is
pub? -
Data struct has
#[derive(AltrepInteger)](or#[derive(Altrep)]for manual pattern)? -
#[altrep(class = "...")]attribute present? -
For manual pattern:
impl AltrepLenprovided? -
For manual pattern:
impl Alt*Dataprovided? -
For manual pattern: used correct
impl_alt*_from_data!macro? -
Ran
devtools::document()? - Reinstalled package?
πPerformance Checklist
-
Provide
no_na()if no NAs β Faster operations -
Provide
is_sorted()if sorted β Faster unique/sort -
Provide
sum()/min()/max()if O(1) possible β Much faster -
Provide
get_region()if faster than looping β Faster bulk access -
Provide
extract_subset()if O(1) subset β Faster subsetting -
Skip
dataptr()if lazy β Save memory -
Implement
dataptr()if in-memory β Faster operations
πSafety Checklist
-
ALTREP derive (
#[derive(Altrep)]or#[derive(AltrepInteger)]etc.) on data struct - No raw pointers outliving their source
-
RefCell(not Mutex) for interior mutability - Returned pointers valid for object lifetime
- SEXPs in lists are protected
- Thread safety: no async/threading in callbacks
πReceiving ALTREP from R
R frequently passes ALTREP vectors to Rust (e.g., 1:10, seq_len(N)).
| Rust parameter | ALTREP handling | Use when |
|---|---|---|
Vec<i32>, &[f64], etc. | Auto-materialized | You need the data |
SEXP | Auto-materialized | You need the handle, not data |
AltrepSexp | Not materialized, !Send | You need ALTREP metadata |
extern "C-unwind" raw SEXP | No conversion at all | You inspect ALTREP state |
// Recommended: typed parameter handles everything
#[miniextendr]
pub fn sum_ints(x: Vec<i32>) -> i64 {
x.iter().map(|&v| v as i64).sum()
}
// sum_ints(1:1000000) β works, ALTREP auto-materialized
// Explicit ALTREP handling
#[miniextendr]
pub fn altrep_len(x: AltrepSexp) -> usize { x.len() }
// altrep_len(1:10) β works
// altrep_len(c(1L, 2L)) β error: not ALTREP
Full guide: Receiving ALTREP from R
πFurther Reading
- Complete guide: ALTREP.md
- Receiving ALTREP: ALTREP_SEXP.md
- Practical examples: ALTREP_EXAMPLES.md
- Test suite: ../rpkg/tests/testthat/test-altrep*.R
- Reference: R Altrep.h