As a developer familiar with JavaScript and TypeScript, delving into Rust felt like stepping into a new realm of elegance and precision. The syntax captivated me, sparking a curiosity to explore its depths. In my Rust learning journey, I ventured from the familiar territory of TypeScript to crafting a simple 'add array' function. Little did I know, this exploration would lead me to the fascinating realms of vectors and arrays. Join me as I share the discoveries and insights gained.
TypeScript
Let's start with the full implementation of the add array function in TypeScript that I wrote first.
typescriptfunction addArrays(arr1: number[], arr2: number[], rarr: number[]): void {for (let i = 0; i < arr1.length; i++) {rarr[i] = arr1[i] + arr2[i];}}function initArray(arr: number[], startVal: number): void {for (let i = 0; i < arr.length; i++) {arr[i] = startVal + i;}}const size: number = 5;let array1: number[] = Array.from({ length: size }, () => 0);let array2: number[] = Array.from({ length: size }, () => 0);let resultantArray: number[] = Array.from({ length: size }, () => 0);initArray(array1, 0);initArray(array2, 10);addArrays(array1, array2, resultantArray);console.log(resultantArray); // [10, 12, 14, 16, 18]
Let's break down the code.
typescriptfunction addArrays(arr1: number[], arr2: number[], rarr: number[]): void {for (let i = 0; i < arr1.length; i++) {rarr[i] = arr1[i] + arr2[i];}}
- This function takes three parameters:
arr1
,arr2
, andrarr
, all of which are arrays of numbers. - It iterates through the arrays using a
for
loop and adds the corresponding elements ofarr1
andarr2
, storing the result inrarr
.
typescriptfunction initArray(arr: number[], startVal: number): void {for (let i = 0; i < arr.length; i++) {arr[i] = startVal + i;}}
- This function initializes an array
arr
with values starting fromstartVal
and incrementing by 1 for each index.
typescriptconst size: number = 5;let array1: number[] = Array.from({ length: size }, () => 0);let array2: number[] = Array.from({ length: size }, () => 0);let resultantArray: number[] = Array.from({ length: size }, () => 0);
size
is a constant variable set to 5, representing the size of the arrays.array1
,array2
, andresultantArray
are initialized as arrays of length size filled with zeros usingArray.from()
.
typescriptinitArray(array1, 0);initArray(array2, 10);addArrays(array1, array2, resultantArray);console.log(resultantArray); // [10, 12, 14, 16, 18]
- The
addArrays
function is called witharray1
,array2
, andresultantArray
. - The final
resultantArray
is logged to the console.
If you familiar with TypeScript I'm sure that this will be easy for you to understand. Now let's rewrite this in Rust.
Rust 🦀
rustfn add_arrays(arr1: &mut Vec<i32>, arr2: &mut Vec<i32>, rarr: &mut Vec<i32>) {for i in 0..arr1.len() {rarr[i] = arr1[i] + arr2[i];}}
- This function takes three parameters:
arr1
,arr2
, andrarr
, all mutable references to vectors of 32-bit signed integers. - It iterates through the vectors using a for loop and adds the corresponding elements of
arr1
andarr2
, storing the result inrarr
.
rustfn init_array(arr: &mut Vec<i32>, start_val: i32) {for i in 0..arr.len() {arr[i] = start_val + i as i32;}}
- This function initializes a vectors (arr) with values starting from
start_val
and incrementing by 1 for each index.
rustfn main() {let size: usize = 5;let mut array1: Vec<i32> = vec![0; size];let mut array2: Vec<i32> = vec![0; size];let mut resultant_array: Vec<i32> = vec![0; size];init_array(&mut array1, 0);init_array(&mut array2, 10);add_arrays(&mut array1, &mut array2, &mut resultant_array);println!("{:?}", resultant_array);}
- The
main
function is the entry point of the Rust program. - It declares a constant variable
size
set to 5, representing the size of the vectors. - Mutable vectors
array1
,array2
, andresultant_array
are initialized with zeros usingvec![0; size]
.vec!
is a macro. - The
init_array
function is called to initializearray1
starting from 0 andarray2
starting from 10. - The
add_arrays
function is called with mutable references toarray1
,array2
, andresultant_array
. - The final
resultant_array
is printed to the console usingprintln!("{:?}", resultant_array)
.
Rust uses vectors and borrows/mutable references to achieve similar functionality while adhering to its ownership and borrowing system.
Exploring Rust, I discovered a simpler approach. The function designed for vectors found a better fit in fixed array, especially since dynamic expansion wasn't needed. Now, let's walk through the process of rewriting the aforementioned function using simple fixed size arrays.
rustfn add_arrays<const N: usize>(arr1: &mut [i32; N], arr2: &mut [i32; N], rarr: &mut [i32; N]) {for i in 0..N {rarr[i] = arr1[i] + arr2[i];}}
- In this version, the function uses arrays
[i32; N]
instead of vectorsVec<i32>
. - The function signature indicates that the size of the arrays is determined at compile time with the generic parameter
const N: usize
, similar to the TypeScript's generic. - It iterates through the arrays using a for loop and adds the corresponding elements of
arr1
andarr2
, similar to the Vec version.
rustfn init_array(arr: &mut [i32], start_val: i32) {for i in 0..arr.len() {arr[i] = start_val + i as i32;}}
- This function initializes a mutable reference to an array
[i32]
instead of a vectorsVec<i32>
. - The initialization process is similar, filling the array with values starting from
start_val
and incrementing by 1 for each index.
rustfn main() {const SIZE: usize = 5;let mut array1 = [0; SIZE];let mut array2 = [0; SIZE];let mut resultant_array = [0; SIZE];init_array(&mut array1, 0);init_array(&mut array2, 10);add_arrays(&mut array1, &mut array2, &mut resultant_array);println!("{:?}", resultant_array);}
- In the main function, arrays
[i32; N]
are used instead of vectorsVec<i32>
. - The arrays are initialized with zeros using the array initializer
[0; SIZE]
, a concise alternative tovec![0; SIZE]
. - The initialization and manipulation process with arrays is similar to the Vec example, showcasing Rust's flexibility with different data structures.
Comparisons
Arrays vs. Vectors
: Arrays have a fixed size determined at compile time, while vectors can dynamically grow or shrink.
Coming from JavaScript and TypeScript, the natural inclination might be to default to vectors, as arrays are commonly used in these languages.
However, exploring arrays in Rust opens up a world of possibilities, offering a more precise and statically-typed alternative for scenarios where the size and type of elements are known at compile time.
Arrays bring a level of predictability and performance that aligns with Rust's focus on memory safety without sacrificing efficiency.
In this transition, embracing the unique features of arrays can enhance code clarity and align more closely with Rust's ownership model and compile-time guarantees.
As a Rust beginner, the journey of discovering the nuances between vectors and arrays has been both enlightening and empowering. Looking ahead, I am eager to share more about my Rust learning experiences. The language's syntax and elegance have captivated me, and I'm excited to explore further, uncovering more insights and challenges along the way. Stay tuned for more tales from my Rust learning adventure!