Expand description
RCow — an R-aware copy-on-write slice.
Like std::borrow::Cow<[T]>, but the borrowed arm
carries the source SEXP it was read from. That single extra field is what
makes a zero-copy round-trip back to R sound, where Cow<[T]> could not
be (see #880).
§The Cow<[T]> hazard this replaces
Cow<[T]> is zero-copy on the way in — TryFromSexp hands back
Cow::Borrowed(&[T]) pointing straight at R’s vector data. But it cannot be
returned to R zero-copy safely. A bare &[T] carries no provenance, so a
borrowed sub-slice (&cow[2..5]) is byte-for-byte indistinguishable from
a full borrow, yet as_ptr() points into the middle of the R vector. The
old recovery path computed data_ptr − SEXPREC_header and speculatively read
there — landing mid-header for a sub-slice (false-positive corruption) or off
the front of a Rust-allocated buffer entirely (segfault — the same class of
bug fixed for Arrow in #867). Arrow could keep zero-copy because
arrow::Buffer proves it is unsliced and R-backed (ptr_offset/
capacity); a &[T] proves nothing.
§How RCow fixes it structurally
RCow::Borrowed wraps RBorrow, whose fields are private. The only
constructor is RCow::try_from_sexp, which always borrows a whole vector.
There is no way to build a borrowed RCow over a sub-range (slice via
Deref to get a plain &[T] instead). So a borrowed RCow always spans
its entire source vector, and IntoR simply returns the stored SEXP — no
pointer arithmetic, no speculative read, no hazard. Need to mutate or reshape?
to_mut / into_owned copy out into an
owned Vec<T>.
§Lifetime contract
As with Cow<[T]>, a borrowed RCow is valid only for the duration of the
enclosing .Call (R protects the argument SEXP while Rust runs). Write
RCow<'_, T> at a #[miniextendr] boundary: the 'a leashes the borrow to
the call, so sending it to another thread or storing it past the call return
is a compile error, not an honor-system pitfall.
Structs§
Enums§
- RCow
- An R-aware copy-on-write slice — the safe, zero-copy-round-trip alternative
to
std::borrow::Cow<[T]>.