Functions
Functions are fundamental building blocks in any programming language. Let’s explore how functions work in Rust and how they compare to JavaScript.
Defining Functions
Section titled “Defining Functions”JavaScript Functions
Section titled “JavaScript Functions”In JavaScript, you can define functions in several ways:
// Function declaration
function add(a, b) {
return a + b;
}
// Function expression
const multiply = function(a, b) {
return a * b;
};
// Arrow function
const subtract = (a, b) => a - b;
Rust Functions
Section titled “Rust Functions”In Rust, functions are defined with the fn
keyword:
fn add(a: i32, b: i32) -> i32 {
a + b
}
Key differences:
- Rust requires type annotations for parameters
- Return type is specified with
->
after the parameter list - The final expression without a semicolon is implicitly returned
- No function expressions or arrow functions in Rust
Function Body and Return Values
Section titled “Function Body and Return Values”JavaScript Return Values
Section titled “JavaScript Return Values”In JavaScript, the return
keyword is used to return a value:
function square(x) {
return x * x;
}
// Functions without return statements return undefined
function greet(name) {
console.log(`Hello, ${name}!`);
// implicitly returns undefined
}
Rust Return Values
Section titled “Rust Return Values”In Rust, the final expression is returned without the return
keyword:
fn square(x: i32) -> i32 {
x * x // No semicolon - this is an expression that returns a value
}
// Explicit return
fn early_return(x: i32) -> i32 {
if x < 0 {
return 0; // Early return requires the return keyword
}
x * x
}
// Functions can return nothing (the unit type)
fn greet(name: &str) {
println!("Hello, {}!", name);
// Implicitly returns the unit type ()
}
The key concept here is that in Rust, statements end with a semicolon and don’t return values, while expressions don’t have a semicolon and do return values.
Function Parameters
Section titled “Function Parameters”JavaScript Parameters
Section titled “JavaScript Parameters”JavaScript is flexible with parameters:
// Optional parameters with default values
function greet(name = "World") {
return `Hello, ${name}!`;
}
// Rest parameters
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}
// Destructuring parameters
function printPerson({ name, age }) {
console.log(`${name} is ${age} years old`);
}
Rust Parameters
Section titled “Rust Parameters”Rust parameters are more strictly typed:
// All parameters require type annotations
fn greet(name: &str) -> String {
format!("Hello, {}!", name)
}
// Multiple parameters
fn calculate_rectangle_area(width: f64, height: f64) -> f64 {
width * height
}
// Default parameters don't exist in the same way as JavaScript
// You can use Option<T> or implement multiple functions
fn greet_option(name: Option<&str>) -> String {
let name = name.unwrap_or("World");
format!("Hello, {}!", name)
}
// Call it with Some or None
let greeting = greet_option(Some("Alice")); // "Hello, Alice!"
let default_greeting = greet_option(None); // "Hello, World!"
Function Overloading
Section titled “Function Overloading”JavaScript doesn’t have true function overloading, though you can simulate it:
function process(arg) {
if (typeof arg === 'number') {
return arg * 2;
} else if (typeof arg === 'string') {
return arg.toUpperCase();
}
return null;
}
Rust doesn’t have function overloading either, but you can:
- Use different function names
- Use generic types (which we’ll cover later)
- Use trait objects (which we’ll cover later)
fn double_number(x: i32) -> i32 {
x * 2
}
fn uppercase_string(s: &str) -> String {
s.to_uppercase()
}
Main Function
Section titled “Main Function”JavaScript doesn’t have a special main function - script execution starts at the top level.
In Rust, the main
function is the entry point for executables:
fn main() {
println!("Hello, world!");
}
Passing Functions as Arguments
Section titled “Passing Functions as Arguments”JavaScript Higher-Order Functions
Section titled “JavaScript Higher-Order Functions”In JavaScript, functions are first-class citizens:
function applyFunction(x, operation) {
return operation(x);
}
// Passing a function as an argument
const result = applyFunction(5, x => x * x); // 25
Rust Function Pointers
Section titled “Rust Function Pointers”Rust also supports passing functions:
fn apply_function(x: i32, operation: fn(i32) -> i32) -> i32 {
operation(x)
}
fn square(x: i32) -> i32 {
x * x
}
// Passing a function as an argument
let result = apply_function(5, square); // 25
// Using closures (similar to lambda expressions)
let result = apply_function(5, |x| x * x); // 25
Closures (Lambda Functions)
Section titled “Closures (Lambda Functions)”JavaScript Closures
Section titled “JavaScript Closures”JavaScript’s closure syntax is lightweight:
const add = (a, b) => a + b;
// Capturing variables from the environment
function makeCounter() {
let count = 0;
return function() {
count += 1;
return count;
};
}
const counter = makeCounter();
console.log(counter()); // 1
console.log(counter()); // 2
Rust Closures
Section titled “Rust Closures”Rust closures use pipes (|
) for parameters:
let add = |a: i32, b: i32| a + b;
// Type inference works with closures
let add_inferred = |a, b| a + b;
let sum = add_inferred(5, 10); // 15
// Capturing variables from the environment
let x = 5;
let add_x = |y| x + y; // Captures x from the environment
let result = add_x(10); // 15
// Mutable closures
let mut counter = 0;
let mut increment = || {
counter += 1;
counter
};
println!("{}", increment()); // 1
println!("{}", increment()); // 2
Rust closures have three traits:
Fn
: Captures by reference (similar to JS closures)FnMut
: Captures by mutable referenceFnOnce
: Captures by value and can only be called once
This is related to Rust’s ownership system, which we’ll cover later.
Methods
Section titled “Methods”JavaScript Methods
Section titled “JavaScript Methods”In JavaScript, methods are functions attached to objects:
class Rectangle {
constructor(width, height) {
this.width = width;
this.height = height;
}
area() {
return this.width * this.height;
}
}
const rect = new Rectangle(10, 20);
console.log(rect.area()); // 200
Rust Methods
Section titled “Rust Methods”In Rust, methods are defined within impl
blocks:
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
// Static method (no self parameter)
fn new(width: u32, height: u32) -> Rectangle {
Rectangle { width, height }
}
// Instance method (uses &self - "self" is like "this" in JavaScript)
fn area(&self) -> u32 {
self.width * self.height
}
// Mutable method (uses &mut self)
fn resize(&mut self, width: u32, height: u32) {
self.width = width;
self.height = height;
}
}
// Usage
let mut rect = Rectangle::new(10, 20);
println!("Area: {}", rect.area()); // 200
rect.resize(30, 40);
println!("New area: {}", rect.area()); // 1200
Associated Functions
Section titled “Associated Functions”These are similar to static methods in JavaScript:
impl Rectangle {
// This is an associated function (no self parameter)
fn square(size: u32) -> Rectangle {
Rectangle {
width: size,
height: size,
}
}
}
// Called with :: syntax, like JavaScript static methods
let square = Rectangle::square(10);
Function Documentation
Section titled “Function Documentation”JavaScript Documentation
Section titled “JavaScript Documentation”JavaScript uses JSDoc comments:
/**
* Calculates the sum of two numbers
* @param {number} a - The first number
* @param {number} b - The second number
* @returns {number} The sum of a and b
*/
function add(a, b) {
return a + b;
}
Rust Documentation
Section titled “Rust Documentation”Rust uses doc comments:
/// Calculates the sum of two numbers
///
/// # Examples
///
/// ```
/// let sum = add(5, 10);
/// assert_eq!(sum, 15);
/// ```
fn add(a: i32, b: i32) -> i32 {
a + b
}
Key Differences
Section titled “Key Differences”- Type Annotations: Rust requires explicit parameter and return types
- Expressions vs. Statements: Rust’s final expression is returned without a
return
keyword - Default Parameters: Rust doesn’t have default parameters like JavaScript
- Function Overloading: Neither language has true function overloading
- Closures: Both languages support closures, but Rust’s have more nuanced capture behavior
- Methods: Rust methods are defined in
impl
blocks and have explicitself
parameters
Next Steps
Section titled “Next Steps”Now that you understand functions in Rust, let’s explore Comments to see how to document your code effectively.