DEV Community

Cover image for Rust for typescript devs: Functions
Rhl
Rhl

Posted on

Rust for typescript devs: Functions

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;
}

Enter fullscreen mode Exit fullscreen mode

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
}

Enter fullscreen mode Exit fullscreen mode

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}!`;
}

Enter fullscreen mode Exit fullscreen mode

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)
}

Enter fullscreen mode Exit fullscreen mode

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);
}

Enter fullscreen mode Exit fullscreen mode

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);
}

Enter fullscreen mode Exit fullscreen mode

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);
}

Enter fullscreen mode Exit fullscreen mode

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);
}

Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

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.

Top comments (0)