Functions are a fundamental part of any programming language, including Stylus, enabling you to encapsulate logic into reusable components.
This guide covers the syntax and usage of functions, including internal and external functions, and how to return multiple values.
Learn More
A function in Stylus consists of a name, a set of parameters, an optional return type, and a body.
Just as with storage, Stylus methods are Solidity ABI equivalent. This means contracts written in different languages are fully interoperable.
Functions are declared with the fn keyword. Parameters allow the function to accept inputs, and the return type specifies the output of the function. If no return type is specified, the function returns () (unit).
Example: a function add that takes two 256-bit integers and returns their sum:
1fn add(a: U256, b: U256) -> U256 {
2 a + b
3}1fn add(a: U256, b: U256) -> U256 {
2 a + b
3}Function parameters are the inputs to a function. They are specified as a list of identifier: Type pairs, separated by commas.
Example with plain Rust integers:
1fn add_numbers(a: u32, b: u32) -> u32 {
2 a + b
3}1fn add_numbers(a: u32, b: u32) -> u32 {
2 a + b
3}Return types define the value produced by a function.
A function with a return type specifies it after ->. In Rust (and Stylus), the last expression is returned implicitly, so return is often omitted.
1pub fn function_name(&self) -> ReturnType {
2 // Function body
3}1pub fn function_name(&self) -> ReturnType {
2 // Function body
3}Function returning a String
1pub fn get_greeting() -> String {
2 "Hello, Stylus!".into()
3}1pub fn get_greeting() -> String {
2 "Hello, Stylus!".into()
3}Function returning an integer
1pub fn get_number() -> u32 {
2 42
3}1pub fn get_number() -> u32 {
2 42
3}Function returning a Result
1pub enum CustomError {
2 ErrorVariant,
3}
4
5pub fn perform_operation(value: u32) -> Result<u32, CustomError> {
6 if value > 0 {
7 Ok(value)
8 } else {
9 Err(CustomError::ErrorVariant)
10 }
11}1pub enum CustomError {
2 ErrorVariant,
3}
4
5pub fn perform_operation(value: u32) -> Result<u32, CustomError> {
6 if value > 0 {
7 Ok(value)
8 } else {
9 Err(CustomError::ErrorVariant)
10 }
11}Public (externally callable) functions are exposed via the contract ABI. In Stylus, you declare them in an impl block annotated with #[public].
Historically, all public methods returned Result<_, Vec<u8>>. This is now optional: if a method is infallible (cannot error), it can return its result directly.
Example (infallible getter):
1#[public]
2impl Contract {
3 pub fn owner(&self) -> Address {
4 self.owner.get()
5 }
6}1#[public]
2impl Contract {
3 pub fn owner(&self) -> Address {
4 self.owner.get()
5 }
6}Internal functions are only callable from within the contract. They live in a regular impl block (no #[public]) and are not part of the external ABI.
Example (internal setter):
1impl Contract {
2 pub fn set_owner(&mut self, new_owner: Address) {
3 self.owner.set(new_owner);
4 }
5}1impl Contract {
2 pub fn set_owner(&mut self, new_owner: Address) {
3 self.owner.set(new_owner);
4 }
5}To mix public and internal functions, use two impl blocks with the same contract name: one annotated with #[public] for external methods, and one without for internal helpers — exactly like in the full example below.
1Loading...1Loading...1Loading...1Loading...1Loading...1Loading...