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

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
var y: u256 = 10 // Mutable (Rust: let mut)
y = 20 // OK

Fe has ownership concepts similar to Rust (own, ref, mut), but references are not values: Fe has mutable value semantics. 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
  • mut grants mutable access to the caller’s place for the duration of a call or binding (let h = mut p.x), similar to &mut T
  • ref grants read access to a place (let r = ref p.x), similar to &T
  • var declares a mutable value, replacing Rust’s let mut

Unlike Rust’s &T and &mut T, ref and mut are not types. An access cannot be stored in a struct field or returned from an ordinary function; a function that returns ref T or mut T is a projection that grants its caller access to a place. An access checker (instead of a borrow checker) rejects overlapping accesses unless both only read, and accesses to different fields of one struct never overlap.

Fe does not require lifetime annotations. Since accesses cannot be stored, an access simply lasts until its last use.

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

Fe’s for loop works over ranges, arrays, MemVec, DynArray and any type implementing the core::iter::Collection trait. Collections are not consumed by a loop; instead the loop’s pattern chooses how each element is bound: for x in xs copies a Copy element, for ref x in xs reads it in place and for mut x in xs modifies it in place.

Collections provide adapters such as enumerate, filter, map, take, skip and reversed for loops, and fold, any, count and find:

// Rust: items.iter().map(|x| x + 1).sum()
let sum = items.fold(0, |acc, x| acc + x + 1)
// Rust: for (i, x) in items.iter().enumerate().filter(..)
for (i, x) in items.enumerate() {
}

See For Loops.

Closures look like Rust’s, |a, b| a + b. A closure captures what it uses by copy or move and cannot modify its captures; state is threaded through parameters and results instead, as fold does. Functions take callables with bounds such as F: Fn(u256) -> u256:

fn apply<F: Fn(u256) -> u256>(_ f: F, _ x: u256) -> u256 uses F::E {
f(x)
}
#[test]
fn closures() {
let add = |a: u256, b: u256| a + b
assert!(add(2, 3) == 5)
assert!(apply(|x| x * 2, 21) == 42)
}

uses F::E forwards the effects the closure’s body needs. See Closures.

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;var x = 1
Returnreturn xreturn x
Selfselfself
Self typeSelfSelf
FeatureStatus in Fe
Ownership/BorrowingMutable value semantics: own, ref, and mut modes, no reference types, no lifetimes (see Mutable Value Semantics)
LifetimesNot needed
ClosuresAvailable; captures are read-only copies or moves
Iteratorsfor over collections with adapters (enumerate, filter, map, …) and fold/any/count/find; no Iterator trait
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
varMutable binding (instead of let mut)
#[selector]ABI selector attributes
#[indexed]Event indexing
StorageMap<K, V>Storage mappings