Rust structs - Unit, Field, and Tuple structs

Structs are one of the most useful features in Rust. Rust structs give programmers the ability to model data structures efficiently. In this article, we’ll go through their basics and see how we can improve our codes with them.

Prerequisites

Before you fully understand this article it will be nice to have the following.

  • Basic understanding of the Rust programming language
  • A Rust compiler installed on your system

What are Structs in Rust?

Rust structs are a method of building data structures in the programming language. Data Structures are the necessary foundation for efficient algorithms and programs.

With the right data structures, your code will take up less memory and runtime. These are useful when creating systems that serve up to millions of users at once.

Rust structures come with several benefits. These benefits include:

  • Modeling complex data structures.
  • Abstracting objects.
  • Binding functions and methods to structures.
  • Binding related methods and data.

Using structs the right way can improve your program’s readability and performance. A well created and utilized struct helps in understanding how data and functions interact with each other in a program.

In the next section, we will take you through the types of structs in Rust.

Types

There are three fundamental types of Rust structs. In this section, we will look at the types.

Field Structs

The following is an example of a field struct:

struct Person {
 name: String,
 age: u8,
}

In this struct, we define each field with its name and type. The Person struct has name and age as its fields. Fields define properties of an object.

To use the struct, you should initialize it first. In initializing the struct, you create an object out of it. The following is an example on initializing the Person struct above:

let person = Person {
 name: "John".to_string(),
 age: 23,
};

The person object is initialized with “John” as name and “23” as age. To reference a field, use the dot (“.”) operator, as shown below:

println!("Name: {}\nAge: {}", person.name, person.age);

You can take a look at the following:

struct Person {
name: String,
age: u8,
}

fn main() {
let person = Person {
  name: "John".to_string(),
  age: 23,
};
println!("Name: {}\nAge: {}", person.name, person.age);
}

The code is a working example that you can run on your system. When you run the code, it gives the following output:

Name: John
Age: 23

Tuple Structs

Tuple structs hold data in the form of a tuple. The benefit of tuple includes not having to come up with names of each field. You only need to provide a datatype during definition:

struct Person (String, u8);

Initializing it is slightly different from initializing a field struct:

let person = Person ("John".to_string(), 23);

The example below shows you how to reference each of the tuple’s elements:

println!("Name: {}\nAge: {}", person.0, person.1);

Run the working example below to see its output:

struct Person (String, u8);
fn main() {
 let person = Person ("John".to_string(), 23);
 println!("Name: {}\nAge: {}", person.0, person.1);
}

Unit Structs

Unlike field and tuple structs, unit structs do not hold any data.

Regardless of its ability to hold data, you can use it for grouping functions and methods. In this article’s The Impl keyword section, you’ll learn how to bind methods to structs.

You declare them like the following:

struct MyUnitStruct;

And initialize them like the following:

let unit_struct = Unit;

Struct Mutability

Mutability of objects is an important concept in the Rust programming language. Rust’s memory management mechanism debates that modifiable variables be put in a separate space from non-modifiable variables.

Mutable structs work the same way as mutable variables. Specify that the variable is mutable with the mut keyword:

let mut person = Person {
 name: "John".to_string(),
 age: 23,
};

Without the mut keyword, you can’t perform operations like the one below:

person.age = 24;

Take a look at the working example below:

struct User {
 name: String,
 is_active: bool,
}

fn main() {
 let mut user = User {
   name: "John".to_string(),
   is_active: false,
 };

 check_active(&user);
 user.is_active = true;
 check_active(&user);
}
fn check_active(user: &User) {
 if user.is_active {
   println!("{} is active", user.name);
 } else {
   println!("{} is not active", user.name);
 }
}

The Impl keyword

The impl keyword is short for “Implementation”. impl is used to define an implementation for Rust structs.

An implementation includes initialization functions, instance methods, and static methods. Impl essentially allows you bind methods to a structure.

Let’s take a look at the example below:

struct Person {
name: String,
age: u8,
}

You can make an implementation like below for the Person struct:

impl Person {
 fn new(name: String, age: u8) -> Person {  // initialization function
   Person {
     name: name,
     age: age,
   }
 }
 fn display(&self) {  // instance method
   println!("Name: {}\nAge: {}", self.name, self.age);
 }
}

In the above, the initialization function, new, creates an instance of the struct. An initialization function is a static method. To call the new function, use the :: operator:

let person = Person::new("John".to_string(), 23);

The instance method, display, can only be called from an instance. To call the method, use the . operator:

person.display();
Check out the working example below:
struct Person {
 name: String,
 age: u8,
}
impl Person {
 fn new(name: String, age: u8) -> Person {
   return Person {
     name: name,
     age: age,
   };
 }
 fn display(&self) {
   println!("Name: {}\nAge: {}", self.name, self.age);
 }
}

fn main() {
 let person = Person::new("John".to_string(), 23);
 person.display();
}

Conclusion

In this tutorial, we walked through creating data structures with struct, and adding functionality to them.

I hope this article gave you a necessary understanding on how rust structs work, and how you can use them to improve your code quality.

Thanks for reading!

Comments

Popular Posts