Named Functions
- Named functions are declared with the keyword
fn - When using arguments, we must declare the data types.
- By default, functions return an empty tuple/
(). If you want to return a value, the return type must be specified after->
Hello world
fn main() {
println!("Hello, world!");
}
Passing Arguments
fn print_sum(a: i8, b: i8) {
println!("sum is: {}", a + b);
}
Returning Values
Without the
returnkeyword. Only the last expression returns.fn plus_one(a: i32) -> i32 { a + 1 // There is no ending ; in the above line. // It means this is an expression which equals to `return a + 1;`. }With the
returnkeyword.fn plus_two(a: i32) -> i32 { return a + 2; // Should use return keyword only on conditional/ early returns. // Using return keyword in the last expression is a bad practice. }
Function Pointers as a Data Type
fn main() {
let p1 = plus_one; // Without type declarations
let a = p1(5); // 6
let p1: fn(i32) -> i32 = plus_one; // With the type declarations
let b = p1(5); // 6
}
fn plus_one(i: i32) -> i32 {
i + 1
}
Closures
- Also known as anonymous functions or lambda functions.
- The data types of arguments and returns are optional β°β±α΅.
Example with a named function, before using closures.
fn main() {
let x = 2;
println!("{}", get_square_value(x));
}
fn get_square_value(i: i32) -> i32 {
i * i
}
With Optional Type Annotations
fn main() {
let x = 2;
let square = |i: i32| -> i32 { // Input parameters are passed inside | | and expression body is wrapped within { }
i * i
};
println!("{}", square(x));
}
Without Type Annotations
fn main() {
let x = 2;
let square = |i| i * i; // { } are optional for single-lined closures
println!("{}", square(x));
}
With Optional Type Annotations; Define and Call Together
fn main() {
let x = 2;
let x_square = |i: i32| -> i32 { i * i }(x); // { } are mandatory while creating and calling same time.
println!("{}", x_square);
}
Without Type Annotations; Define and Call Together
fn main() {
let x = 2;
let x_square = |i| -> i32 { i * i }(x); // βοΈ The return type is mandatory.
println!("{}", x_square);
}
Test Functions
- Start the function name with the
test_prefix. - Add with the
#[test]attribute, inside atestsmodule with the#[cfg(test)]attribute.
fn greet() -> String {
"Hello, world!".to_string()
}
#[cfg(test)]
mod tests {
use super::greet; // π‘ Reimport the greet() function from the parent module.
#[test]
fn test_greet() { // The test function of greet()
assert_eq!("Hello, world!", greet());
}
}
π¨βπ« Before going to the next…
π― Usage of
::and.to call functions in different modules,π This is a quick reference about the usage of
::and.operators while calling functions. So, please donβt worry about structs, enums, traits, or impls for now. We will discuss them later.Functions are standalone blocks of code, declare with the
fnkeyword.Associated functions are functions that are associated with a particular data type such as structs, enums, or traits via an
implblock.Methods are associated functions with a receiver of
self,&self,&mut self,self: Box<Self>etc.
βοΈ To call methods: use the
.operator from an instance. ex.steve.intro_name()βοΈ To call associated functions that are not methods: use the
::operator from the data type. ex.Person::new(),String::from()struct Person { name: String, company_name: String, } impl Person { // π‘ impls are used to define functions in Rust structs, enums, etc. // π‘ The constructor (new` is a conventional name, not a keyword) fn new(name: String, company_name: String) -> Person { // an associated function and not a method Person { name, company_name } } fn intro_name(&self) -> String { // π‘ a method format!("I'm {}", self.name) // π‘ access fields via `.` operator } fn intro_company(&self) -> String { // π‘ a method format!("I'm from {}", self.company_name) } } fn main() { // π‘ calling associated functions with `::` operator let steve = Person::new(String::from("Steve Jobs"), String::from("Apple")); // π‘ calling methods with `.` operator println!("{}. {}.", steve.intro_name(), steve.intro_company()); // I'm Steve Jobs. I'm from Apple. // βοΈ methods are also associated functions. So, we can call them with `::` operator as well but need to pass the instance as a parameter. println!("{}. {}.", Person::intro_name(&steve), Person::intro_company(&steve)); // I'm Steve Jobs. I'm from Apple. }- Other than that,
::operator is used to call functions in different modules.
mod my_mod { pub fn greet(name: &str) { println!("Hello, {name}!") } } fn main() { my_mod::greet("Steve Jobs"); // Hello, Steve Jobs! }π Refer path separator and member access operators for more information about the usage of the
::and.operators.