Skip to content
This guide targets Fe 26.4. Fe is not production-ready. Install Fe 26.4

Fe for Rust Developers

Fe draws heavy inspiration from Rust. This guide highlights what’s familiar and what’s different for Rust developers.

Fe structs work like Rust structs:

struct Point {
x: u256,
y: u256,
}
let p = Point { x: 10, y: 20 }
let x = p.x

Methods are defined in impl blocks:

struct Counter {
value: u256,
}
impl Counter {
fn new() -> Self {
Counter { value: 0 }
}
fn increment(mut self) {
self.value += 1
}
fn get(self) -> u256 {
self.value
}
}

Traits define shared behavior:

trait Hashable {
fn hash(self) -> u256
}
impl Hashable for Point {
fn hash(self) -> u256 {
self.x ^ self.y
}
}

Type parameters work similarly:

fn identity<T>(value: own T) -> T {
value
}
struct Wrapper<T> {
value: T,
}
impl<T> Wrapper<T> {
fn get(own self) -> T {
self.value
}
}

Constrain generics with trait bounds:

fn process<T: Hashable>(item: T) -> u256 {
item.hash()
}
fn complex<T: Hashable + Printable>(item: T) {
// T must implement both traits
}

Enums with match expressions:

enum Status {
Pending,
Active,
Completed { result: u256 },
}
fn handle(status: Status) -> u256 {
match status {
Status::Pending => 0,
Status::Active => 1,
Status::Completed { result } => result,
}
}

Optional values use Option<T>:

let maybe: Option<u256> = Option::Some(42)
match maybe {
Option::Some(v) => v,
Option::None => 0,
}

Most constructs are expressions:

let value: u256 = if condition { 10 } else { 20 }
let result = match status {
Status::Active => true,
_ => false,
}

Types are inferred where possible:

let x = true // Type inferred as bool
let y: u8 = 42 // Explicit annotation

Variables are immutable by default:

let x: u256 = 10 // Immutable
let mut y: u256 = 10 // Mutable
y = 20 // OK

Fe has ownership concepts similar to Rust (own, ref, mut) but with key differences:

  • Default is view mode (no keyword): the function can read the value but not move or modify it. Unlike Rust, view-mode parameters can be used multiple times without explicit borrowing:
fn process(data: MyStruct) {
// View mode: read-only access, no ownership transfer
}
let a = MyStruct { x: 1 }
process(data: a)
process(data: a) // Fine: view mode doesn't consume the value
  • own transfers ownership (like Rust’s move): the caller cannot use the value after passing it
  • ref creates a read-only reference that can be stored in struct fields (like Rust’s &T)
  • mut creates a mutable handle (like Rust’s &mut T)

Fe also has a borrow checker that prevents simultaneous ref and mut access to the same data.

Fe does not require lifetime annotations. References are scoped but the compiler manages lifetimes implicitly.

See Ownership & Mutability for details on own, ref, and mut.

Fe’s for loop works over ranges, arrays, and any type implementing the core::seq::Seq trait (a length plus indexed get). There is no Rust-style Iterator trait with adapters such as map, filter, or collect:

// Rust: items.iter().map(|x| x + 1).sum()
// Fe: loop and accumulate explicitly
let mut sum: u256 = 0
for item in items {
sum = sum + item + 1
}

Arrays are iterable only when their element type is Copy; see For Loops.

Fe doesn’t support closures:

// Rust: let add = |a, b| a + b;
// Fe: Use named functions
fn add(a: u256, b: u256) -> u256 {
a + b
}

Fe has modules, but uses different terminology:

RustFe
CrateIngot
ModuleModule
// In fe.toml
[dependencies]
my_lib = { path = "../my_lib" }

Fe organizes code into ingots (packages) containing modules, similar to Rust’s crate/module system.

Fe has first-class contract support:

contract Token {
mut store: TokenStorage
init(supply: u256) uses (mut store) {
}
recv TokenMsg {
}
}

External interfaces are defined separately:

use std::abi::sol
msg TokenMsg {
#[selector = sol("transfer(address,uint256)")]
Transfer { to: Address, amount: u256 } -> bool,
}

Persistent key-value storage:

struct Storage {
balances: StorageMap<Address, u256>,
}

Blockchain events for logging:

#[event]
struct Transfer {
#[indexed]
from: Address,
#[indexed]
to: Address,
value: u256,
}

Explicit capability tracking:

fn transfer(from: Address, to: Address, amount: u256)
uses (store: mut TokenStore, log: mut Log)
{
// Function declares what it accesses
}
ConceptRustFe
Functionfn foo() {}fn foo() {}
Structstruct Foo {}struct Foo {}
Implimpl Foo {}impl Foo {}
Traittrait Bar {}trait Bar {}
Genericsfn foo<T>()fn foo<T>()
BoundsT: TraitT: Trait
Matchmatch x {}match x {}
Ifif x {}if x {}
Looploop {}while true {}
Forfor x in iter {}for x in iter {}
Letlet x = 1;let x = 1
Mutlet mut x = 1;let mut x = 1
Returnreturn xreturn x
Selfselfself
Self typeSelfSelf
FeatureStatus in Fe
Ownership/BorrowingSimplified: own, ref, and mut modes; no lifetimes (see Simplified Ownership Model)
LifetimesNot needed
ClosuresNot available
Iteratorsfor over ranges, arrays, and Seq types; no iterator adapters
async/awaitNot applicable
MacrosNo user-defined macros; assert! is built in
FeatureDescription
contractSmart contract declarations
msgMessage type definitions
recvMessage handlers
initContract constructors
usesEffect declarations
withEffect binding
#[selector]ABI selector attributes
#[indexed]Event indexing
StorageMap<K, V>Storage mappings