pub trait NativeFnCaps: Send + Sync {
// Required method
fn call_relon(
&self,
func: &Value,
args: Vec<Value>,
range: TokenRange,
) -> Result<Value, RuntimeError>;
// Provided methods
fn max_value_elements(&self) -> Option<usize> { ... }
fn next_iter_id(&self) -> u64 { ... }
fn iter_cursor_fetch_and_inc(
&self,
_iter_id: u64,
_len: usize,
) -> Option<usize> { ... }
fn tick(&self, _n: u64, _range: TokenRange) -> Result<(), RuntimeError> { ... }
}Expand description
A handle to the evaluator’s internal execution capabilities, allowing native functions to call back into Relon logic (closures).
Lives in relon-eval-api so that any backend implementing
crate::Evaluator can mint a NativeFnCaps of its own for the native
fns it dispatches. The default impls keep host-supplied RelonFunctions
usable in lightweight test contexts where no backend is attached.
Required Methods§
fn call_relon( &self, func: &Value, args: Vec<Value>, range: TokenRange, ) -> Result<Value, RuntimeError>
Provided Methods§
Sourcefn max_value_elements(&self) -> Option<usize>
fn max_value_elements(&self) -> Option<usize>
Expose Capabilities::max_value_elements to native functions
so collection-building intrinsics (range, future bulk
constructors) can pre-flight oversized requests before
allocating. Returning None means the host imposes no cap on
List / Tuple / Dict element counts.
The evaluator still runs a post-call check_value_size on
every List / Tuple / Dict produced by a native fn (catch-all in
call_function / try_call_native_method), so an intrinsic
that ignores this hint is still bounded — but allocating
Vec::with_capacity(end - start) first would OOM the host
before the post-call check fires. Intrinsics that build a
collection whose size is known up-front from their arguments
should consult this and reject early.
Sourcefn next_iter_id(&self) -> u64
fn next_iter_id(&self) -> u64
Mint a fresh Iter cursor id under the originating Context.
Used by List.iter() / String.iter() / Dict.iter() (and
any future user-side Iterable constructor that wants to
participate in Iter.next() cursor tracking) to stamp the
_id field of the resulting Iter-branded dict. Returns
0 from the default impl so a non-Context-backed caps
(e.g. in unit tests) still produces a sane id; production
evaluation overrides this on the per-Context impl.
Sourcefn iter_cursor_fetch_and_inc(&self, _iter_id: u64, _len: usize) -> Option<usize>
fn iter_cursor_fetch_and_inc(&self, _iter_id: u64, _len: usize) -> Option<usize>
Atomic read-check-increment of the cursor associated with
iter_id. Returns Some(old_cursor) when the cursor was
strictly less than len (and the cursor is post-incremented
in the same critical section), or None when the cursor has
reached len or the id is unknown to this Context.
The “unknown id ⇒ None” branch is the cross-Context
isolation policy: an Iter value built in Context A and then
handed to Context B looks exhausted from B’s perspective.
Implementations should preserve this — silently auto-inserting
a fresh cursor for an unknown id would re-introduce ambient
state across Context boundaries.
Sourcefn tick(&self, _n: u64, _range: TokenRange) -> Result<(), RuntimeError>
fn tick(&self, _n: u64, _range: TokenRange) -> Result<(), RuntimeError>
Advance the step counter by n and bail with
RuntimeError::StepLimitExceeded if the new count would exceed
max_steps. Native fns with internal loops (range,
list.map / filter / reduce, string.split / replace,
dict.merge, …) call this once per inner iteration so a
million-element pipeline can’t hide behind a single AST-node
step.
Behaviour:
max_steps == None→ no-op (Ok(())), no allocation, no lock — the default impl below mirrors that for hosts that build a customNativeFnCaps.max_steps == Some(limit)→fetch_add(n)on the same atomic counter the evaluator increments; if the new value crosses the limit, returnStepLimitExceeded { limit, range }.
range should pin the call-site span of the intrinsic so the
resulting diagnostic points at the same node the AST-level step
check would have flagged.
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".