Chapter 17: Rust Functions
Rust Functions.
This is a big milestone! Functions are where code starts to become reusable, organized, and real-world useful. In Rust, functions feel very clean and strict — types everywhere, explicit returns, no hidden magic. Rust forces you to think about ownership & borrowing even in simple functions, which is why it feels “different” at first but becomes super powerful later.
Let me explain like your patient teacher sitting next to you with VS Code open: slowly, from basic to advanced, with lots of copy-paste examples you can run right now, Hyderabad-flavored comments, and comparisons so you see why Rust does things this way.
1. Basic Function Syntax – Defining & Calling
Every function starts with fn keyword.
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 |
fn main() { say_namaste(); // calling the function say_namaste(); // call again — reuse! } fn say_namaste() { println!("Namaste Hyderabad! Learning Rust functions today 🚀"); } |
- fn = keyword to define function
- say_namaste = name (snake_case convention — all lowercase + underscores)
- () = parentheses — always required, even with no parameters
- { … } = body
- Call it by name + ()
Rust doesn’t care where you define functions — above or below main is fine.
2. Functions with Parameters (Arguments)
Parameters = special variables inside the function. You must write their types (no inference here — safety!).
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 |
fn main() { greet("Webliance", 25); greet("Hyderabad", 500081); // pin code as u32 example } fn greet(name: &str, age: u32) { println!("Hello {}, you are {} years old!", name, age); } |
- name: &str — type after colon
- Multiple params separated by comma
- &str = string slice (cheap borrow — very common for text)
3. Returning Values – Two Styles
Rust functions can return values. Return type after ->.
Style 1: Explicit return (like most languages)
|
0 1 2 3 4 5 6 7 8 |
fn add(a: i32, b: i32) -> i32 { return a + b; } |
Style 2: Implicit return (Rust favorite — no return, no 😉
|
0 1 2 3 4 5 6 7 8 |
fn multiply(x: i32, y: i32) -> i32 { x * y // last expression = returned value } |
- Last line without ; = return value
- You can mix — return early, implicit at end
Full example:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
fn main() { let sum = add(10, 25); let product = multiply(4, 7); println!("Sum: {}, Product: {}", sum, product); } fn add(a: i32, b: i32) -> i32 { a + b // implicit return } fn multiply(x: i32, y: i32) -> i32 { if x == 0 || y == 0 { return 0; // early explicit return } x * y // implicit if no early return } |
4. Functions with Ownership & Borrowing (Rust Magic Starts Here)
Remember ownership? Functions take ownership or borrow.
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
fn main() { let s = String::from("Hello"); take_ownership(s); // s moved — s invalid after this // println!("{}", s); // ERROR! let len = calculate_length(&s); // borrow → s still valid println!("Still have: {}, length: {}", s, len); } fn take_ownership(text: String) { // takes ownership println!("Took ownership: {}", text); // text dropped at end of function } fn calculate_length(s: &String) -> usize { // borrow immutable s.len() } |
- String (no &) → move ownership
- &String → immutable borrow
- &mut String → mutable borrow (only one at a time!)
5. Returning Multiple Values (Tuples!)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 |
fn main() { let (sum, product) = math_operations(5, 3); println!("5 + 3 = {}, 5 * 3 = {}", sum, product); } fn math_operations(a: i32, b: i32) -> (i32, i32) { (a + b, a * b) } |
Destructuring makes it clean.
6. Function Signatures & Documentation (Good Practice)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
/// Calculates area of a rectangle. /// /// # Examples /// /// ``` /// let area = rectangle_area(4.0, 5.0); /// assert_eq!(area, 20.0); /// ``` fn rectangle_area(width: f64, height: f64) -> f64 { width * height } |
- /// = doc comment (shows in cargo doc)
- Parameters & return type mandatory in signature
7. Associated Functions (Like static methods)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
struct Rectangle { width: f64, height: f64, } impl Rectangle { fn area(&self) -> f64 { // method — &self borrow self.width * self.height } fn square(size: f64) -> Rectangle { // associated function (no self) Rectangle { width: size, height: size, } } } fn main() { let rect = Rectangle { width: 30.0, height: 50.0 }; println!("Area: {}", rect.area()); let sq = Rectangle::square(10.0); // called on type println!("Square area: {}", sq.area()); } |
Teacher Summary Table
| Feature | Syntax Example | Key Notes |
|---|---|---|
| No params, no return | fn hello() { … } | Always () |
| Params | fn greet(name: &str) | Types required |
| Return value | -> i32 | Implicit last expr or return |
| Ownership transfer | fn take(s: String) | Moves — caller loses it |
| Borrow immutable | fn len(s: &String) -> usize | Safe read-only |
| Borrow mutable | fn increment(n: &mut i32) | One mutable borrow only |
| Multiple returns | -> (i32, i32) | Use tuple + destructuring |
| Method | fn area(&self) in impl | self, &self, &mut self |
Practice tip: Create cargo new rust_functions Write 5–6 small functions:
- greet with name & city
- calculate circle area
- is_even checker
- swap two numbers (return tuple)
- temperature converter (°C → °F)
Want to go deeper next?
- Methods vs functions in detail?
- Closures (anonymous functions)?
- Ownership & borrowing inside functions (the heart of Rust)?
- Or generics in functions?
Just tell me — your Rust class from Hyderabad is going strong! 🦀🚀
