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

Trait Definition

Traits define shared behavior that types can implement. They’re Fe’s way of expressing interfaces and enabling polymorphism, similar to traits in Rust or interfaces in other languages.

Define a trait with the trait keyword:

trait Drawable {
fn draw(self)
}

This defines a trait called Drawable with one required method draw.

Traits declare methods that implementing types must provide:

trait Summary {
fn summarize(self) -> String
}
trait Comparable {
fn compare(self, other: Self) -> i32
}
trait Container {
fn len(self) -> u256
fn is_empty(self) -> bool
}

Each method signature specifies:

  • The method name
  • Parameters (including self)
  • Return type

In trait definitions, Self refers to the implementing type:

trait Cloneable {
fn clone(self) -> Self // Returns the same type that implements this trait
}
trait Addable {
fn add(self, other: Self) -> Self // Works with two values of the same type
}

Traits can require multiple methods:

trait Token {
fn balance_of(self, account: u256) -> u256
fn transfer(mut self, to: u256, amount: u256) -> bool
fn total_supply(self) -> u256
}
trait Owned {
fn owner(self) -> u256
fn transfer_ownership(mut self, new_owner: u256)
fn renounce_ownership(mut self)
}

Make traits public with pub:

pub trait Serializable {
fn serialize(self) -> Array<u8>
fn deserialize(data: Array<u8>) -> Self
}

Public traits can be implemented by types in other modules.

Trait methods can have various parameter types:

trait Calculator {
// Only self
fn value(self) -> u256
// Self and primitives
fn add(mut self, amount: u256)
// Self and custom types
fn apply(mut self, config: Config)
// Mutable self
fn reset(mut self)
}

Each trait should represent one capability:

// Good: focused traits
trait Readable {
fn read(self) -> u256
}
trait Writable {
fn write(mut self, value: u256)
}
// Less ideal: combined responsibilities
trait ReadWritable {
fn read(self) -> u256
fn write(mut self, value: u256)
}

Design traits that can be combined:

trait Identifiable {
fn id(self) -> u256
}
trait Nameable {
fn name(self) -> String
}
trait Timestamped {
fn created_at(self) -> u256
fn updated_at(self) -> u256
}
// A type can implement all three
trait ERC20 {
fn total_supply(self) -> u256
fn balance_of(self, account: u256) -> u256
fn transfer(mut self, to: u256, amount: u256) -> bool
fn allowance(self, owner: u256, spender: u256) -> u256
fn approve(mut self, spender: u256, amount: u256) -> bool
fn transfer_from(mut self, from: u256, to: u256, amount: u256) -> bool
}
trait Validator {
fn is_valid(self) -> bool
}
// Now generic functions can accept any Validator
fn process<T: Validator>(item: T) -> bool {
item.is_valid()
}
trait Hashable {
fn hash(self) -> u256
}
trait Comparable {
fn equals(self, other: Self) -> bool
fn less_than(self, other: Self) -> bool
}
SyntaxDescription
trait Name { }Define a trait
pub traitPublic trait
fn method(self)Required method
fn method(mut self)Mutating method
SelfThe implementing type
-> TypeMethod return type