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
| Expression Name | Example | Support | Comments |
|---|---|---|---|
| Array literal | [a, b] | Supported | |
| Assignment | a = b; | Supported | |
| Async block | async { ... } | Unsupported | |
| Await | foo().await | Unsupported | |
| Binary expression | a + b | Partial Support | See table below for supported binary expressions. |
| Block expression | { ... } | Supported | |
| Break | break; | Unsupported | |
| Function call | f(a, b) | Partial Support | See table below for supported functions. |
| Casts | a as u16 | Partial Support | See table below for supported conversions. |
| Closures | |a, b | a + b | Supported | |
| Const block | const { ... } | Supported | |
| Continue | continue; | Unsupported | |
| Field access/set | obj.field | Supported | |
| For loop | for i in expr { ... } | Supported | Note that expr will have its length known at compile-time. |
| If | if cond { ... } else { ... } | Supported | Complexity is O(then_block + else_block). |
| Indexing | a[idx] | Supported | Complexity is O(a.len()) if idx isn’t compile-time known (all positions are checked to hide which index was accessed). |
| If let | if let Some(x) = ... | Partial Support | See pattern matching. Let chains require Rust edition 2024. |
| Literals | 1u128 | Partial Support | See table below for supported literals. |
| Loops | loop { ... } | Unsupported | MPC circuits have fixed structure—variable iteration counts would require dynamic circuit size. Use for with compile-time bounds instead. |
| Macros | println!("{}", q) | Partial Support | See table below for supported macros. |
| Match | match n { ... } | Partial Support | See pattern matching. Last arms cannot have guards. |
| Method calls | x.foo(a, b) | Partial Support | See table below for supported methods. |
| Parentheses | (a + b) | Supported | |
| Paths | Foo::bar | Partial Support | See table below for supported paths. |
| Ranges | 4..5 | Partial Support | Not supported in arr[4..16]. |
| Raw addresses | &raw const foo | Unsupported | |
| References | &mut foo | Supported | |
| Repeat arrays | [4u8; 128] | Supported | |
| Return | return false; | Unsupported | |
| Struct literals | MyStruct { a: 12, b } | Supported | |
| Try expression | this_call_can_err()?; | Unsupported | |
| Tuple literal | (a, 4, c) | Supported | |
| Unary expressions | !x | Partial Support | User-defined unary operations are not supported. |
| Unsafe | unsafe { ... } | Unsupported | |
| While loops | while x < 64 { ... } | Unsupported | Cannot be supported as the number of iterations is not known. |
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.| Example | Supported types |
|---|---|
a + b | Integers, floats, BaseField25519 |
a - b | Integers, floats, BaseField25519 |
a * b | Integers, floats, BaseField25519 |
a / b | Integers, floats |
a % b | Integers |
a && b | Booleans |
a || b | Booleans |
a ^ b | Booleans |
a & b | Booleans |
a | b | Booleans |
a << b | None |
a >> b | Integers, if b is known at compile time. |
a == b | All. Use derive(PartialEq) for structs. |
a != b | All. Use derive(PartialEq) for structs. |
a < b | Booleans, integers, floats, BaseField25519 |
a <= b | Booleans, integers, floats, BaseField25519 |
a >= b | Booleans, integers, floats, BaseField25519 |
a > b | Booleans, integers, floats, BaseField25519 |
a += b | Integers, floats, BaseField25519 |
a -= b | Integers, floats, BaseField25519 |
a *= b | Integers, floats, BaseField25519 |
a /= b | Integers, floats |
a %= b | Integers |
a ^= b | Booleans |
a &= b | Booleans |
a |= b | Booleans |
a <<= b | None |
a >>= b | Integers, if b is known at compile time |
BaseField25519 does not support /, %, >>, or <<. Note that &, |, ^ 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:
| From Type | To Type |
|---|---|
| integer type | integer type |
bool | integer type |
| integer type | bool |
&...&T | &T |
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_attemptsmust be compile-time known. Returns(result, success)wheresuccess=falseindicates all attempts were rejected. Withn_attempts=24, failure probability is<2^-24.ArcisRNG::shuffle(slice)on slices. Complexity is inO(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 confidential data witharcis_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() -> BaseField25519to 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_i8…from_i128) andfrom_bool,from_usize,from_isizealso available.BaseField25519::power_of_two(exp)to compute2^expas a field element.
Literal expressions
| Example | Support |
|---|---|
"foo" | Unsupported |
b"foo" | Supported |
c"foo" | Unsupported |
b'f' | Supported |
'a' | Unsupported |
1 | Supported |
1u16 | Supported |
1f64 | Supported |
1.0e10f64 | Supported |
true | Supported |
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 returnbool. 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 enablecrate::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")orencrypted_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.
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 allCloneobjects..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..sort()on arrays of integers. Complexity is inO(n*log²(n)*bit_size)..enumerate(),.chain(other),.cloned(),.copied(),.count(),.rev(),.zip(other),.map(func),.for_each(func),.fold(init, func),.sum(),.product()on iterators..take(n),.skip(n),.step_by(n)on iterators whennis compile-time known..reveal()if not inside a conditionally executed block (if/else, non-constantmatcharm, or guard).to_arcis()onEncs.from_arcis(x)onOwners (objects of typesMxeorShared) if not inside a conditionally executed block (if/else, non-constantmatcharm, 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()on typed integers (does not work on integers whose type the interpreter does not know).exp(),.exp2(),.ln(),.log2(),.sqrt()on floats..unpack()onPack<T>to extract the original value from packed storage..to_arcis_with_pubkey_and_nonce(pubkey, nonce)onEncData<T>to reveal when the key is shared across inputs (avoids duplicate reveal gates). See EncData for details..dataonEnc<Owner, T>to extract only theEncData<T>ciphertext for smaller callback payloads..safe_inverse()onBaseField25519to get the field inverse (returns 0 for inverse of 0)..field_division(divisor)onBaseField25519for field division (returns 0 for division by 0)..euclidean_division(divisor)onBaseField25519for signed Euclidean division (panics on division by 0)..to_u8_unchecked()….to_u128_unchecked()onBaseField25519to extract as unsigned int. Silently produces incorrect results if value exceeds target range. Signed variants (to_i8_unchecked…to_i128_unchecked) andto_bool_uncheckedalso available.
Paths
The following paths are supported:IntType::BITS,IntType::MINandIntType::MAXwhereIntTypeis an integer type.- Paths to user-defined constants, functions and structs, as long as they are inside the
#[encrypted]area. Bothsuper::paths andcrate::paths (whenassert_current_module!is declared) are supported. std::mem::replaceandstd::mem::swapBox::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:
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
| Item Name | Example | Support | Comments |
|---|---|---|---|
| Constant | const MAX: u16 = 65535 | Supported | |
| Enum | enum MyEnum { ... } | Unsupported | |
| Extern | extern ... | Unsupported | |
| Functions | fn foo() -> u8 { 0 } | Partial Support | Recursive functions are not supported. |
| Impls | impl MyType { ... } | Supported | Generics and custom traits are supported. MyType must not be a reference. |
| Macro Definitions | macro_rules! ... | Unsupported | |
| Macro Invocations | println!(...) | Partial Support | See table above for supported macros. |
| Modules | mod my_module { ... } | Supported | |
| Statics | static ... | Unsupported | |
| Structs | struct MyStruct { ... } | Supported | |
| Traits | trait MyTrait { ... } | Partial Support | Custom traits with associated types and constants. Standard library traits forbidden.¹ |
| Type Aliases | type MyId = usize; | Supported | |
| Union | union MyUnion { ... } | Unsupported | |
| Use | use arcis::* | Partial Support | Only use arcis::* is supported. |
| Arcis Circuit | #[arcis_circuit = "name"] | Supported | Use a pre-built optimized circuit by name. For internal/advanced use. |
¹ Forbidden trait implementations: You cannot manually implement
Drop, Deref, AsRef, AsMut, From, Into, TryFrom, TryInto, PartialEq, Eq, PartialOrd, Ord, Clone, ToOwned, ToString, Default, 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, etc., which generates MPC-compatible implementations.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 supportsmatch 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
| Feature | Supported | Notes |
|---|---|---|
Type parameters <T> | Yes | Must be known at compile time |
Trait bounds T: Trait | Yes | Including ArcisType |
| Associated types | Yes | type Output; |
| Associated constants | Yes | const SIZE: usize; |
| Where clauses | Yes | where T: Clone |
Turbofish ::<T> | Yes | For explicit type specification |
| Runtime polymorphism | No | No dyn Trait or trait objects |
Iterators
Most iterator methods work in Arcis, with the notable exception of.filter().
Supported iterator methods
Complete iterator support
| Method | Supported | Notes |
|---|---|---|
.iter() | Yes | Creates iterator of references |
.iter_mut() | Yes | Mutable references |
.into_iter() | Yes | Consumes collection |
.map(f) | Yes | Transform elements |
.enumerate() | Yes | Add indices |
.zip(other) | Yes | Pair with another iterator |
.chain(other) | Yes | Concatenate iterators |
.rev() | Yes | Reverse order |
.cloned() | Yes | Clone elements |
.copied() | Yes | Copy elements |
.fold(init, f) | Yes | Reduce with accumulator |
.sum() | Yes | Sum all elements |
.product() | Yes | Multiply all elements |
.count() | Yes | Count elements |
.take(n) | Yes | n must be compile-time known |
.skip(n) | Yes | n must be compile-time known |
.step_by(n) | Yes | n must be compile-time known |
.for_each(f) | Yes | Apply function to each |
.filter(f) | No | Would produce variable-length output |
.find(f) | No | Would require early exit |
.any(f) | No | Would require early exit |
.all(f) | No | Would require early exit |
Filter alternative
Since.filter() is not supported (it produces variable-length output), use a manual loop with conditionals:
What’s next?
Primitives
RNG, cryptography, and data packing operations.
Best practices
Performance tips, debugging, and testing strategies.