Skip to main content
Arcis supports many of Rust’s native operations and extends them for encrypted data, allowing you to write private computations using familiar Rust syntax. See the tables below for a detailed list of supported and unsupported operations.
Use this page when you need to check if a specific operation is supported in Arcis circuits.

Quick summary

Works: if/else, if let, match, for loops, arithmetic, comparisons, iterators (except filter) Doesn’t work: while, loop, break, continue, return, let ... else, .filter() See tables below for full details.

Table of contents

Expression support

Why branches count: In MPC, both sides of non-constant conditional execution are evaluated, including if/else branches and non-constant match arms. The condition only selects which result to use. This ensures the execution pattern does not leak information about the condition value. See Thinking in MPC for details.

Binary expressions

User-defined binary operations are currently unsupported.
BaseField25519 does not support /, %, >>, or <<. &, |, ^ are booleans-only across all types. Use .field_division() or .euclidean_division() for division on field elements. See BaseField25519 Operations.

Cast expressions

a as MyType is only supported:

Function calls

The following function calls are supported:
  • user-defined function calls (without recursion)
  • ArcisRNG::bool() to generate a boolean.
  • ArcisRNG::gen_uniform::<T>() to generate a uniform value of type T (bool, integer, or combination). Requires explicit type parameter.
  • ArcisRNG::gen_integer_from_width(width: usize) -> u128. Generates a secret integer between 0 and 2^width - 1 included.
  • ArcisRNG::gen_public_integer_from_width(width: usize) -> u128. Generates a public integer between 0 and 2^width - 1 included.
  • ArcisRNG::gen_integer_in_range(min: u128, max: u128, n_attempts: usize) -> (u128, bool). Generates a random integer in [min, max] using rejection sampling. n_attempts must be compile-time known. Returns (result, success) where success=false indicates all attempts were rejected. With n_attempts=24, failure probability is <2^-24.
  • ArcisRNG::shuffle(slice) on slices. Complexity is in O(n*log³(n) + n*log²(n)*sizeof(T)).
  • Mxe::get() to be able to create MXE-owned secret data.
  • Shared::new(arcis_public_key) to share private data with arcis_public_key.
  • ArcisX25519Pubkey::from_base58(base58_byte_string) to create a public key from a base58-encoded address.
  • ArcisX25519Pubkey::from_uint8(u8_byte_slice) to create a public key from a Uint8 array.
  • SolanaPublicKey::from_serialized(value) to create a Solana public key from serialized form.
  • SolanaPublicKey::from_base58(byte_string) to create a Solana public key from base58.
  • ArcisMath::sigmoid(x) for the sigmoid activation function.
  • LogisticRegression::new(coef, intercept) for logistic regression models.
  • LinearRegression::new(coef, intercept) for linear regression models.
  • Pack::new(value) to bit-pack data for onchain storage (multiple small values fit into fewer field elements).
  • ArcisX25519Pubkey::new_from_x(x: BaseField25519) to create a public key from its Curve25519 Montgomery X-coordinate.
  • ArcisX25519Pubkey::to_x() -> BaseField25519 to extract the Montgomery X-coordinate from a public key.
  • BaseField25519::from_u8(x)BaseField25519::from_u128(x) to convert unsigned integers to field elements. Signed variants (from_i8from_i128) and from_bool, from_usize, from_isize also available.
  • BaseField25519::power_of_two(exp) to compute 2^exp as a field element.

Literal expressions

Macros

The following macros are supported:
  • debug_assert!, debug_assert_ne!, debug_assert_eq! to assert conditions during debugging. They do not change instruction behavior.
  • eprint!, eprintln!, print!, println! to print debug output. They do not change instruction behavior.
  • matches!(expr, pattern) to test a pattern and return bool. See Pattern support.
  • arcis_static_panic!(message) to fail compilation when the branch is reached. Useful for enforcing constraints that must be known before circuit generation.
  • include_bytes!("file_path") to include raw bytes from a file in Arcis circuits.
  • include!("file_path") to include a file in item position (not expression position).
  • assert_current_module!(crate::path::to::module) to enable crate:: absolute paths within the current module. Place at the top of any module that needs to reference items via absolute paths.
  • encrypted_mod!("path/to/module.rs") or encrypted_mod!("path/to/module.rs", alias_name) to use another file as a module within an #[encrypted] module. The target file must use the #[encrypted_library] attribute. Items are accessible via the filename stem (or alias) as a namespace, e.g., module_name::ITEM.
Example usage:
arcis_static_panic! triggers at compile time when the Arcis compiler evaluates the branch. Try changing ARRAY_LEN to 1 above: the compile error demonstrates how this macro enforces constraints that must be validated before circuit generation.

Method calls

The following method calls are supported:
  • user-defined method calls (with generics but without recursion)
  • .clone() on all Clone objects.
  • .len(), .is_empty(), .swap(a, b), .fill(value), .reverse(), .iter(), .iter_mut(), .into_iter(), .windows(width), .copy_from_slice(src), .clone_from_slice(src), .split_at(mid), .split_at_mut(mid), .rotate_left(mid), .rotate_right(mid), .contains(item), .starts_with(needle), .ends_with(needle), .as_slice(), .as_mut_slice() on arrays and slices.
  • .first(), .first_mut(), .last(), .last_mut(), .get(index), .get_mut(index), .split_first(), .split_first_mut(), .split_last(), .split_last_mut(), .first_chunk::<N>(), .first_chunk_mut::<N>(), .last_chunk::<N>(), .last_chunk_mut::<N>(), .split_first_chunk::<N>(), .split_first_chunk_mut::<N>(), .split_last_chunk::<N>(), .split_last_chunk_mut::<N>() on arrays and slices. These return Option.
  • .as_array::<N>(), .as_mut_array::<N>() on slices (requires Rust 1.93+). These return Option.
  • .map(f), .each_ref(), .each_mut() on arrays.
  • .sort() on arrays of integers. Complexity is in O(n*log²(n)*bit_size).
  • .enumerate(), .chain(other), .cloned(), .copied(), .count(), .rev(), .zip(other), .map(func), .for_each(func), .fold(init, func), .sum(), .product(), .collect::<Box<[_]>>() on iterators.
  • .take(n), .skip(n), .step_by(n) on iterators when n is compile-time known.
  • .reveal() if not inside a conditionally executed block (if/else, non-constant match arm, or guard)
  • .to_arcis() on Encs
  • .from_arcis(x) on Owners (objects of types Mxe or Shared) if not inside a conditionally executed block (if/else, non-constant match arm, or guard)
  • .abs(), .min(x), .max(x) on integers and floats
  • .abs_diff(other), .is_positive(), .is_negative(), .div_ceil(other) on integers
  • .to_le_bytes(), .to_be_bytes(), .wrapping_add(rhs), .wrapping_sub(rhs), .wrapping_mul(rhs) on typed integers (does not work on integers whose type the interpreter does not know)
  • .exp(), .exp2(), .ln(), .log2(), .sqrt() on floats.
  • .set(val), .swap(other), .update(f), .replace(val), .into_inner(), .get(), .get_mut() on Cell.
  • .unpack() on Pack<T> to extract the original value from packed storage.
  • Option<T> combinators: .is_some(), .is_none(), .is_some_and(f), .is_none_or(f), .unwrap(), .unwrap_or(default), .unwrap_or_else(f), .map(f), .map_or(default, f), .map_or_else(default_f, f), .inspect(f), .filter(predicate), .and(other), .and_then(f), .or(other), .or_else(f), .xor(other), .zip(other), .unzip(), .flatten(), .copied(), .cloned(), .take(), .take_if(predicate), .replace(value), .insert(value), .get_or_insert(value), .get_or_insert_with(f).
  • .to_arcis_with_pubkey_and_nonce(pubkey, nonce) on EncData<T> to decrypt when the key is shared across inputs (avoids duplicate decryption gates). See EncData for details.
  • .data on Enc<Owner, T> to extract only the EncData<T> ciphertext for smaller callback payloads.
  • .safe_inverse() on BaseField25519 to get the field inverse (returns 0 for inverse of 0).
  • .field_division(divisor) on BaseField25519 for field division (returns 0 for division by 0).
  • .euclidean_division(divisor) on BaseField25519 for signed Euclidean division (panics on division by 0).
  • .to_u8_unchecked().to_u128_unchecked() on BaseField25519 to extract as unsigned int. Silently produces incorrect results if value exceeds target range. Signed variants (to_i8_uncheckedto_i128_unchecked) and to_bool_unchecked also available.

Paths

The following paths are supported:
  • IntType::BITS, IntType::MIN and IntType::MAX where IntType is an integer type.
  • Paths to user-defined constants, functions and structs, as long as they are inside the #[encrypted] area. Both super:: paths and crate:: paths (when assert_current_module! is declared) are supported.
  • std::mem::replace and std::mem::swap
  • Box::leak, Box::new, Cell::new, Cell::from_mut

Code organization with modules

Arcis supports nested modules, super:: parent references, crate:: absolute paths, and multi-file projects. #[instruction] functions can be placed at any module depth, not just at the top level. Submodules with super:: and crate:: paths:
Multi-file projects with encrypted_mod!:
Each file imported via encrypted_mod! must use #[encrypted_library] (not #[encrypted]). Items are accessible through the filename stem as a namespace (e.g., helpers::Config), or you can provide an alias: encrypted_mod!("helpers.rs", utils) makes items accessible as utils::Config.

Item support

¹ Forbidden trait implementations: You cannot manually implement Drop, Deref, AsRef, AsMut, From, Into, TryFrom, TryInto, PartialEq, Eq, PartialOrd, Ord, Clone, ToOwned, ToString, Iterator, IntoIterator, DoubleEndedIterator, ExactSizeIterator, Extend, FromIterator, Fn, FnMut, FnOnce, Future, IntoFuture, AsyncFn, AsyncFnMut, or AsyncFnOnce.Why? These traits have special runtime semantics (drop ordering, lazy evaluation, dynamic dispatch) that cannot be correctly translated to fixed MPC circuits. The Arcis compiler provides built-in implementations that work within MPC constraints.Use #[derive(...)] for Clone, PartialEq, Default, etc., which generates MPC-compatible implementations. Default also supports a manual impl Default for T.

Pattern support

The following patterns are supported in function arguments, let statements, if let conditions, matches! calls, and match expressions:
  • simple idents: let ident = ...;
  • mutable idents: let mut ident = ...;
  • ref idents: let ref ident = ...;
  • mutable ref idents: let ref mut ident = ...;
  • parentheses around a supported pattern: let (...) = ...;
  • reference of a supported pattern: let &... = ...;
  • array of supported patterns: let [...] = ...;
  • struct of supported patterns: let MyStruct { ... } = ...;
  • tuple of supported patterns: let (...) = ...;
  • tuple struct of supported patterns: let MyStruct(...) = ...;
  • type pattern of a supported pattern: let ...: ty = ...;
  • wild pattern: let _ = ...;
| patterns are supported only in if let, matches!, and match; they cannot be used on a match arm that also has a guard. The .. pattern is only supported inside struct patterns with named fields, e.g. MyStruct { x: 0, .. }. Literal, range, and path-constant patterns are only supported in if let, matches!, and match. Path constants must use a path such as module::MY_CONST; bare const identifiers are not supported as patterns.

Pattern matching

Arcis supports match expressions, if let (including let chains), and the matches! macro for branching on patterns. Patterns can be literals, ranges, path constants like module::MY_CONST, OR-patterns, tuples, structs (with .. rest), arrays/slices, references, bindings, and wildcards. Match arms can use if-guards, except an arm cannot combine a guard with an OR-pattern.
let ... else remains unsupported. In match, guards are supported, but an arm cannot combine a guard with an OR-pattern.

match expressions

if let and let chains

Let chains (if let ... && let ... && ...) require Rust edition 2024. The default arcium init scaffold sets edition = "2021" in encrypted-ixs/Cargo.toml. Bump it to edition = "2024" to use this syntax.

matches! macro

matches! returns a bool and supports the same pattern surface:

Slice patterns

Fixed-size arrays viewed as slices can be matched with arms of different lengths:
Item shadowing of a let binding is rejected. Defining fn f() or const F: _ after let f = ... (or let F = ...) errors at compile time with “Cannot have an item with the same name as a variable in scope.” let shadowing another let is still allowed.

Generics

Arcis supports Rust generics with some constraints. Generic types must be known at compile time. Runtime polymorphism is not supported.

Generic functions

Generic structs

Custom traits

Generic constraints

Iterators

Most iterator methods work in Arcis, with the notable exception of .filter().

Supported iterator methods

Complete iterator support

Filter alternative

Since .filter() is not supported (it produces variable-length output), use a manual loop with conditionals:
This pattern checks all elements but only accumulates those meeting the condition: same result, fixed execution structure.

What’s next?

Primitives

RNG, cryptography, and data packing operations.

Best practices

Performance tips, debugging, and testing strategies.