SYSTEM PROGRAMMING

From C to Rust (Part III)

Fundamental Data Structures: struct, union, array

TechExplorer
Level Up Coding
Published in
6 min readMar 10, 2023

--

Introduction

In the previous articles of this series, we discussed some of the minor syntax differences between C and Rust, as well as the preprocessor and pointers. In this third and final part of the series, we will be exploring complex data structures in both languages, such as structs, unions, and arrays.

While the syntax may differ slightly between C and Rust, the concepts remain the same. Understanding how these data structures work in both languages is crucial for developers transitioning from C to Rust. Developers can write efficient and effective code by mastering these data structures in Rust.

If you’re short on time or want to quickly see the examples in action, we’ve included a Summary section, at the end of the article, that covers all three data structures. Additionally, if you want to try the Rust code yourself, we recommend using an online Rust playground, which allows you to experiment with Rust code without installing anything on your machine.

A quick recap of the differences

Structs

In C, structs are used to define a collection of related variables that can be accessed as a single entity. In Rust, structs work in a similar way, but with some syntax differences. Rust structs use the keyword struct followed by the name of the struct and a block of curly braces that define the struct's fields. Here's an example:

struct Person {
name: String,
age: u32,
address: String,
}

This defines a Person a struct with three fields: name, age, and address. In C, we would define a similar struct using the struct keyword followed by the name of the struct and a block of curly braces that define the struct's fields. Here's an example:

struct Person {
char *name;
int age;
char *address;
};

As you can see, the syntax is slightly different, but the concept is the same. In Rust, a comma is used to separate fields instead of a semi-colon, and a double colon ‘:’ is used between the type and name of the variable. Additionally, in C, struct declarations are closed with a semi-colon.

In both languages, we can create instances of the struct and access its fields using dot notation:

let person = Person {
name: String::from("John Doe"),
age: 30,
address: String::from("123 Main St."),
};

println!("Name: {}", person.name);
println!("Age: {}", person.age);
println!("Address: {}", person.address);
struct Person person = {"John Doe", 30, "123 Main St."};

printf("Name: %s\n", person.name);
printf("Age: %d\n", person.age);
printf("Address: %s\n", person.address);

Unions

In C, unions are used to store different data types in the same memory location. In Rust, unions work in a similar way, but with some syntax differences. Rust unions use the keyword union followed by the name of the union and a block of curly braces that define the union's fields. Here's an example:

union Data {
i: i32,
f: f32,
c: char,
}

This defines a Data union with three fields: i, f, and c. In C, we would define a similar union using the union keyword followed by the union's name and a block of curly braces that define the union's fields. Here's an example:

union Data {
int i;
float f;
char c;
};

As you can see, the syntax is slightly different, but the concept is the same. The differences are similar to those of struct.

In both languages, we can create instances of the union and access its fields using dot notation:

let mut data = Data { i: 10 };
unsafe {
println!("The value of i is: {}", data.i);
}
union Data data;
data.i = 10;
printf("The value of i is: %d\n", data.i);

In Rust, we need to use the unsafe keyword to access the fields of a union because Rust's type system cannot guarantee the safety of accessing different types in the same memory location.

Arrays

In both C and Rust, arrays are used to store a collection of elements of the same data type. In C, we define an array using square brackets and a size, like this:

int numbers[5] = {1, 2, 3, 4, 5};

In Rust, we define an array using square brackets and the type of the elements, like this:

let numbers: [i32; 5] = [1, 2, 3, 4, 5];

Array declarations in Rust differ slightly from those in C, but the concept of storing a collection of elements remains the same. In Rust, arrays are declared using square brackets ‘[ ]’, whereas in C, square brackets are used in both declaration and usage. Rust arrays also use a colon ‘:’ to specify the type of the elements, while in C, the type is specified before the name of the array. Additionally, Rust arrays have a fixed length specified at compile-time, whereas in C, arrays can have a fixed or variable length (allocated on the stack).

In both languages, we can access individual elements of the array using square brackets and the index of the element, like this:

println!("The first number is {}", numbers[0]);
printf("The first number is %d\n", numbers[0]);

In Rust, we can also use the for loop to iterate over the elements of an array:

for number in numbers.iter() {
println!("{}", number);
}

In C, we would use a for loop with an index variable to iterate over the elements of an array:

for (int i = 0; i < 5; i++) {
printf("%d\n", numbers[i]);
}

Summary

C example grouping all data structures:

#include <stdio.h>

struct Person {
char *name;
int age;
char *address;
};
union Data {
int i;
float f;
char c;
};
int main() {
// Struct Example
struct Person person = {"John Doe", 30, "123 Main St."};
printf("Name: %s\n", person.name);
printf("Age: %d\n", person.age);
printf("Address: %s\n\n", person.address);
// Union Example
union Data data;
data.i = 10;
printf("The value of i is: %d\n", data.i);
data.f = 3.14;
printf("The value of f is: %f\n", data.f);
data.c = 'A';
printf("The value of c is: %c\n\n", data.c);
// Array Example
int numbers[5] = {1, 2, 3, 4, 5};
for (int i = 0; i < 5; i++) {
printf("%d\n", numbers[i]);
}
return 0;
}

The equivalent in Rust:

struct Person {
name: String,
age: u32,
address: String,
}

union Data {
i: i32,
f: f32,
c: char,
}

fn main() {
// Struct Example
let person = Person {
name: String::from("John Doe"),
age: 30,
address: String::from("123 Main St."),
};
println!("Name: {}", person.name);
println!("Age: {}", person.age);
println!("Address: {}\n", person.address);
// Union Example
let mut data = Data { i: 10 };
unsafe {
println!("The value of i is: {}", data.i);
}
data.f = 3.14;
unsafe {
println!("The value of f is: {}", data.f);
}
data.c = 'A';
unsafe {
println!("The value of c is: {}\n", data.c);
}
// Array Example
let numbers: [i32; 5] = [1, 2, 3, 4, 5];
for number in numbers.iter() {
println!("{}", number);
}
}

Conclusion

Understanding complex data structures such as structs, unions, and arrays are crucial for any developer transitioning from C to Rust. While the syntax may differ slightly between the two languages, the concepts are the same. By mastering these data structures in both languages, developers can write efficient and effective code in Rust. In this article, we explored the syntax and usage of these data structures in both C and Rust, providing developers with a solid understanding of how to use them in their Rust code. With this knowledge, developers can confidently make the transition from C to Rust and write high-quality code in this modern programming language.

If you found this article helpful, please give it a clap and consider following us for more content on programming languages, tools, and techniques. Learning a new language can be challenging, but with the right resources and community, it can also be a rewarding experience. We encourage you to keep exploring Rust and see how it can improve your systems programming skills and projects. Thank you for reading, and happy coding!

--

--

Tech enthusiast with an interest in software and a love for cryptocurrencies. Check out my crypto donation page at: https://rb.gy/mqd43h