Skip to content
Pre-Release: Fe is under active development. This documentation covers the upcoming release. Follow progress on GitHub

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(from sender: u256, to recipient: u256, amount: u256) {
// Inside the function, use: sender, recipient, amount
}
// At the call site, use the labels:
transfer(from: alice, to: 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: 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 with mutable self - can read and modify
fn increment(mut self) -> Self {
self.value = self.value + 1
self
}
}

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: 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 (mut storage: 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