Strict mode rejects lossy type conversions that would silently widen or truncate values. When enabled, out-of-range values panic instead of being coerced.

πŸ”—What It Affects

Strict mode applies to lossy integer types only: types where R’s native integer range (i32, excluding NA_integer_) cannot represent all values:

TypeScalarVecOptionVec<Option>Option<Vec>Vec<Vec>Vec<Option<Vec>>
i64YesYesYesYesYesYesYes
u64YesYesYesYesYesYesYes
isizeYesYesYesYesYesYesYes
usizeYesYesYesYesYesYesYes

The last three columns are return types only. Strict returns also support Box<[T]>, &[T], [T; N], their optional forms, and list rows held in slices, boxes or arrays (Vec<&[T]>, Vec<Option<&[T]>>, Vec<Box<[T]>>, Vec<[T; N]>, including optional boxed/array rows). They use the same integer range checks and batch every failing element across all rows. A missing container or row is NULL; an empty container or row is an empty integer vector.

Types like i32, f64, String, bool are not affected. They have lossless R representations.

πŸ”—Behavior Comparison

πŸ”—Output (Rust β†’ R)

Normal mode: Values outside i32 range silently widen to f64 (REALSXP).

// Normal: 2^40 silently becomes double
let big: i64 = 1_099_511_627_776;
big.into_sexp()  // β†’ REALSXP (double)

Strict mode: Values outside i32 range panic.

#[miniextendr(strict)]
pub fn strict_big() -> i64 {
    1_099_511_627_776  // PANIC: i64 value outside R integer range
}

Valid range: (i32::MIN, i32::MAX] (note i32::MIN (-2147483648) is excluded because it represents NA_integer_ in R).

πŸ”—Input (R β†’ Rust)

Normal mode: Accepts INTSXP, REALSXP, RAWSXP, and LGLSXP with coercion.

# Normal: logical TRUE coerced to i64
my_func(TRUE)   # OK β†’ 1i64
my_func(1L)     # OK β†’ 1i64
my_func(1.0)    # OK β†’ 1i64
my_func(as.raw(1))  # OK β†’ 1i64

Strict mode: Only accepts INTSXP and REALSXP. Rejects RAWSXP and LGLSXP. REALSXP values must be whole numbers in range.

# Strict: only numeric types accepted
strict_func(1L)     # OK
strict_func(1.0)    # OK (whole number)
strict_func(1.5)    # PANIC: fractional value
strict_func(TRUE)   # PANIC: expected integer or double, got LGLSXP
strict_func(as.raw(1))  # PANIC: expected integer or double, got RAWSXP

πŸ”—How to Enable

πŸ”—Per-Function

#[miniextendr(strict)]
pub fn exact_value(x: i64) -> i64 { x }

πŸ”—Per-Impl Block

#[miniextendr(s3, strict)]
impl Calculator {
    pub fn compute(&self, n: u64) -> u64 { n * 2 }
}

πŸ”—Per-Function Override

#[miniextendr(strict)]               // impl-level: strict
impl Calculator {
    pub fn normal_method(&self, x: i64) -> i64 { x }     // strict (inherited)

    #[miniextendr(no_strict)]
    pub fn relaxed_method(&self, x: i64) -> i64 { x }    // not strict (overridden)
}

πŸ”—Project-Wide Default

In Cargo.toml:

[dependencies]
miniextendr-api = { version = "0.1", features = ["strict-default"] }

When strict-default is enabled, all #[miniextendr] functions default to strict. Use #[miniextendr(no_strict)] to opt out.

πŸ”—Error Messages

A rejected input (a logical, raw, fractional, out-of-range or NA value for a lossy integer parameter) is an argument error, the same condition as any other failed conversion (kind = "conversion", e$param, e$rust_type; see ERROR_HANDLING.md). A vector reports every failing element at once, with R’s 1-based positions:

'count' must be a single whole number: got logical
'ids' must be integer or whole-number numeric: precision loss (elements 2, 4); NA is not allowed (element 3)

A return value that does not fit an R integer is a panic (kind = "panic"), caught and converted to an R error; a vector names its Rust type and the positions in the returned vector:

strict conversion failed: i64 value 1099511627776 is outside R integer range
(-2147483647..=2147483647); use a non-strict function to allow lossy f64 widening
strict conversion failed for Vec<i64>: i64 value 1099511627776 is outside R integer
range (-2147483647..=2147483647) (element 2); use a non-strict function to allow lossy
f64 widening

A list of vectors (Vec<Vec<usize>>) checks every row before it fails, and names each failing value’s row (element) and its place in the row:

strict conversion failed for Vec<Vec<usize>>: usize value 3000000000 exceeds R
integer max (2147483647) at inner position 1 (element 2); use a non-strict function
to allow lossy f64 widening

πŸ”—When to Use Strict Mode

Use strict when:

  • Building numeric-critical APIs where silent precision loss is a bug
  • Working with database IDs, timestamps, or bit flags that must be exact
  • You want to catch accidental type coercions during development

Skip strict when:

  • Values are known to be small (fit in i32)
  • Approximate f64 representation is acceptable
  • You want maximum R interoperability (accept any numeric-like input)

πŸ”—See Also