Rust - Functions
Rust - Functions
Section titled “Rust - Functions”Functions are the fundamental building blocks of a Rust program. They encapsulate a specific task, making code reusable, readable, and maintainable. Functions organize a program into logical, self-contained units.
Defining and Calling Functions
Section titled “Defining and Calling Functions”You define a function in Rust using the fn keyword, followed by the function name, a set of parentheses () for parameters, and curly braces {} for the function body. Function names use snake_case by convention.
// The `main` function is the entry point of every Rust executable.fn main() { println!("Hello from main function!");
// Call another function another_function();}
// Define another functionfn another_function() { println!("Hello from another function!");}Output
Section titled “Output”Hello from main function!Hello from another function!Function Parameters
Section titled “Function Parameters”Functions can accept inputs called parameters. In the function signature, you must declare the name and type of each parameter. This allows you to pass values into the function to be processed.
fn main() { print_value(10); print_sum(5, 6);}
// A function with one parameter of type i32fn print_value(x: i32) { println!("The value is: {}", x);}
// A function with two parametersfn print_sum(x: i32, y: i32) { println!("The sum is: {}", x + y);}Statements vs. Expressions and Return Values
Section titled “Statements vs. Expressions and Return Values”Rust function bodies are made up of a series of statements ending in an optional expression. Statements are instructions that perform an action but do not return a value (e.g., let y = 6;). Expressions evaluate to a resulting value (e.g., 5 + 6).
A function can return a value to the code that called it. The return type is declared after an arrow ->. The value returned is the value of the final expression in the function body. You can also use the return keyword to return early from a function.
fn main() { let sum = add_five(10); println!("10 + 5 = {}", sum);
let product = multiply(8, 7); println!("8 * 7 = {}", product);}
// This function returns an i32.// The line `x + 5` is an expression, and because it's the last line,// its value is returned automatically.fn add_five(x: i32) -> i32 { x + 5 // No semicolon here!}
// This function also returns an i32, but uses the `return` keyword.fn multiply(x: i32, y: i32) -> i32 { return x * y; // Semicolon is optional here but good practice.}Output
Section titled “Output”10 + 5 = 158 * 7 = 56Passing Parameters: Ownership vs. Borrowing
Section titled “Passing Parameters: Ownership vs. Borrowing”The way you pass parameters to a function interacts directly with Rust’s ownership system. Instead of “pass-by-value” and “pass-by-reference”, it is more accurate to think in terms of moving ownership or borrowing.
1. Passing by Value (Move)
Section titled “1. Passing by Value (Move)”When you pass a variable to a function, ownership of the data is moved to the function’s parameter. The original variable can no longer be used. This applies to types that don’t implement the Copy trait, like String or Vec<T>.
fn main() { let s = String::from("hello"); takes_ownership(s);
// The line below would fail to compile because `s`'s value was moved. // println!("{}", s);}
fn takes_ownership(some_string: String) { println!("I now own the string: {}", some_string);} // `some_string` is dropped here and its memory is freed.2. Passing by Reference (Borrowing)
Section titled “2. Passing by Reference (Borrowing)”To use a value without taking ownership, you can pass a reference to it. This is called borrowing. The caller retains ownership. References are immutable by default.
fn main() { let s1 = String::from("hello");
// Pass an immutable reference to the string. let len = calculate_length(&s1);
println!("The length of '{}' is {}.", s1, len); // `s1` is still valid and can be used here.}
// This function borrows a String instead of taking ownership.fn calculate_length(s: &String) -> usize { s.len()} // `s` goes out of scope, but the data it refers to is not dropped.3. Passing a Mutable Reference (Mutable Borrow)
Section titled “3. Passing a Mutable Reference (Mutable Borrow)”If you need to modify the borrowed value, you can pass a mutable reference using &mut. You can only have one mutable reference to a particular piece of data in a particular scope.
fn main() { let mut s = String::from("hello"); println!("Original string: {}", s);
// Pass a mutable reference. change(&mut s);
println!("Modified string: {}", s);}
fn change(some_string: &mut String) { some_string.push_str(", world");}Output
Section titled “Output”Original string: helloModified string: hello, world