Skip to content
Fe 26.0 is not production-ready. This is an initial release of a new compiler. Learn more

Functions

Functions are the building blocks of Fe programs. They encapsulate reusable logic and provide structure to your code.

Declare a function using the fn keyword:

fn greet() {
// function body
}

A function with parameters and a return type:

fn add(a: u256, b: u256) -> u256 {
a + b
}

Parameters are declared with a name and type:

fn process(value: u256, flag: bool) {
// use value and flag
}

Fe supports labeled parameters for improved call-site clarity. A label is the name used when calling the function, while the parameter name is used inside the function body:

fn transfer(sender: u256, recipient: u256, amount: u256) {
// Inside the function, use: sender, recipient, amount
}
// At the call site, use the labels:
transfer(sender: alice, recipient: bob, amount: 100)

This makes function calls self-documenting and reduces errors when functions have multiple parameters of the same type.

Use _ as the label when you don’t want to require a label at the call site:

fn square(_ x: u256) -> u256 {
x * x
}
// Call without a label:
let result = square(5)

You can mix labeled and unlabeled parameters:

fn create_point(_ x: u256, _ y: u256, named: bool) -> Point {
// x and y are positional, named requires a label
}
// Call:
create_point(10, 20, named: true)

Use mut to declare a parameter that can be modified within the function:

fn increment(mut value: own u256) -> u256 {
value = value + 1
value
}

Note that this creates a mutable local copy; it doesn’t modify the caller’s variable.

Functions that operate on a type instance use self as their first parameter, making them methods:

struct Counter {
value: u256,
}
impl Counter {
// Method with immutable self - can read but not modify
fn get(self) -> u256 {
self.value
}
// Method that returns an updated counter
fn increment(self) -> Self {
Counter { value: self.value + 1 }
}
}

Methods are called using dot notation:

let mut counter = Counter { value: 0 }
counter = counter.increment()
let current = counter.get()
ReceiverDescription
selfImmutable access to the instance
mut selfMutable access to the instance

Specify a return type with -> after the parameters:

fn calculate(x: u256) -> u256 {
x * 2
}

The last expression in a function is implicitly returned (without a semicolon):

fn double(x: u256) -> u256 {
x * 2 // implicitly returned
}

Use return for early returns or explicit clarity:

fn abs(x: i256) -> i256 {
if x < 0 {
return -x
}
x
}

Functions without -> return the unit type (similar to void in other languages):

fn log_value(value: u256) {
// no return value
}

Functions are private by default, accessible only within their module.

Use pub to make a function accessible from other modules:

pub fn public_function() {
// accessible from other modules
}
fn private_function() {
// only accessible within this module
}

In Fe, contracts cannot contain regular functions. Contracts only have:

  • Storage fields for state
  • An init block for initialization
  • recv blocks for handling incoming messages

Functions are defined separately, either as free-floating functions or as methods on structs. Code inside recv blocks can call these external functions:

// Free-floating helper function
fn calculate_fee(amount: u256) -> u256 {
amount / 100
}
// Helper struct with methods
struct TokenLogic {}
impl TokenLogic {
pub fn validate_transfer(self, amount: u256) -> bool {
amount > 0
}
}
contract MyToken {
mut store: Storage,
recv TransferMsg {
Transfer { to, amount } -> bool uses (mut store) {
// Call free-floating function
let fee = calculate_fee(amount)
// Recv block handles the message
true
}
}
}

This separation keeps contracts focused on state and message handling, while logic lives in reusable functions and structs.

Functions can be generic over types using angle brackets:

fn identity<T>(value: own T) -> T {
value
}

With trait bounds:

fn print_value<T: Display>(value: T) {
// T must implement Display
}

For comprehensive coverage of generics and trait bounds, see the Traits & Generics section.

Fe uses an effect system to track side effects. Functions declare their effects with the uses clause:

fn read_storage() uses (storage: Storage) {
// can read from storage
}
fn write_storage() uses (storage: mut Storage) {
// can read and write to storage
}

For comprehensive coverage of effects, see the Effects section.

FeatureSyntaxExample
Basic functionfn name() { }fn greet() { }
With parametersfn name(a: T) { }fn add(x: u256) { }
With return typefn name() -> T { }fn get() -> u256 { }
Labeled parameterlabel name: Tfrom sender: u256
Unlabeled parameter_ name: T_ x: u256
Mutable parametermut name: Tmut count: u256
Method (immutable)fn name(self)fn get(self) -> u256
Method (mutable)fn name(mut self)fn set(mut self, v: u256)
Public functionpub fn name()pub fn api() { }
Generic functionfn name<T>()fn id<T>(x: T) -> T
With effectsfn name() uses Efn read() uses Storage