Rust for typescript devs: Functions
Rhl
Posted on September 2, 2024
Hi, I am Rahul and I learnt rust to build Loadjitsu.io. This is the fourth post in my series on "Rust for typescript devs".
Read the third part about Strings here.
Read the intro post here.
Functions are the building blocks of any programming language, allowing you to encapsulate logic and reuse code effectively. As a TypeScript developer, you’re familiar with how functions work, but Rust introduces a few differences in syntax and functionality. In this section, we’ll explore how functions in Rust compare to those in TypeScript, using examples to highlight the similarities and differences.
Basic Function Syntax
TypeScript:
In TypeScript, you define a function using the function
keyword, followed by the function name, parameters, and return type. TypeScript’s function syntax is straightforward and familiar to anyone who has worked with JavaScript.
function add(a: number, b: number): number {
return a + b;
}
Rust:
In Rust, functions are defined using the fn
keyword, followed by the function name, parameters, and return type. While the syntax is different, the underlying concept is similar.
fn add(a: i32, b: i32) -> i32 {
a + b
}
Similarity: Both languages require you to define the function's parameters and return type. In Rust, the return type is specified after an arrow ->
, and the last expression in the function is returned by default, without needing a return
keyword (although you can use return
explicitly if you wish).
Function Parameters and Return Types
TypeScript:
In TypeScript, function parameters and return types are annotated with their types, helping to catch errors at compile time.
function greet(name: string): string {
return `Hello, ${name}!`;
}
Rust:
Rust also requires you to specify the types of function parameters and return values, ensuring type safety.
fn greet(name: &str) -> String {
format!("Hello, {}!", name)
}
Key Difference: Rust's greet
function uses &str
for the parameter type (a string slice) and String
for the return type. This distinction between string slices and owned strings is a key part of Rust’s type system, ensuring efficient memory usage and safety.
Functions Without Return Values
Both TypeScript and Rust allow you to write functions that perform an action without returning a value.
TypeScript:
In TypeScript, such functions are typically annotated with a return type of void
.
function logMessage(message: string): void {
console.log(message);
}
Rust:
In Rust, functions that don’t return a value have a return type of ()
(the unit type), which is similar to void
in TypeScript.
fn log_message(message: &str) {
println!("{}", message);
}
Similarity: In both languages, you can create functions that perform actions without returning a value. In Rust, the absence of a return type is indicated by the unit type ()
.
Function Overloading
TypeScript:
TypeScript supports function overloading, allowing you to define multiple signatures for the same function. This is useful when a function can be called with different types or numbers of arguments.
function display(value: string): void;
function display(value: number): void;
function display(value: string | number): void {
console.log(value);
}
Rust:
Rust does not support traditional function overloading. However, you can achieve similar behavior using traits or enum to handle different types of input.
fn display(value: &str) {
println!("{}", value);
}
fn display_number(value: i32) {
println!("{}", value);
}
Key Difference: In Rust, you would typically define separate functions or use traits to handle different types of inputs. While TypeScript’s function overloading allows for more flexible function definitions, Rust’s approach emphasizes clarity and explicitness.
Closures
Both TypeScript and Rust support closures, which are functions defined within another function. Closures can capture variables from their surrounding environment.
TypeScript:
In TypeScript, closures are commonly used with anonymous functions or arrow functions.
function makeAdder(x: number) {
return function(y: number): number {
return x + y;
};
}
const add5 = makeAdder(5);
console.log(add5(10)); // Output: 15
Rust:
In Rust, closures are defined using the ||
syntax, and they can capture variables from the surrounding scope.
fn make_adder(x: i32) -> impl Fn(i32) -> i32 {
move |y| x + y
}
let add5 = make_adder(5);
println!("{}", add5(10)); // Output: 15
Similarity: Both TypeScript and Rust allow you to create closures that capture and use variables from their surrounding environment. In Rust, the move
keyword can be used to take ownership of the captured variables.
Higher-Order Functions
Higher-order functions, which take other functions as arguments or return them, are supported in both TypeScript and Rust.
TypeScript:
function applyFunction(f: (x: number) => number, value: number): number {
return f(value);
}
function square(x: number): number {
return x * x;
}
console.log(applyFunction(square, 5)); // Output: 25
Rust:
fn apply_function<F>(f: F, value: i32) -> i32
where
F: Fn(i32) -> i32,
{
f(value)
}
fn square(x: i32) -> i32 {
x * x
}
println!("{}", apply_function(square, 5)); // Output: 25
Similarity: Both languages allow you to pass functions as arguments to other functions. In Rust, the Fn
trait is used to define the type of the function argument, ensuring type safety.
Posted on September 2, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 29, 2024