What Is a Structs In Rust?

What are Structs?

A struct is short for structure. It’s like a container that groups different pieces of data under one name. Structs are similar to objects in object-oriented programming, but a struct itself doesn’t have methods; it only stores data.

  • It can hold different types of values, such as a number, a string, a Boolean.
  • It’s like creating your own “data model” (such as a student, book, or car).

Types of Structs in Rust

Rust provides us with different styles of structs, depending on how we want to represent that data.

1) Named Field Structs: This is the most common type of struct. Each field inside the struct has a name and a data type.

For example, if you are building a library system, you may want to store information about a book, such as its title, the number of pages, and its price.

Code example:

struct Book {
title: String,
pages: u32,
price: f32,
}

fn main() {
let book1 = Book {
title: String::from("Rust Guide"),
pages: 250,
price: 19.99,
};

println!("{} has {} pages and costs ${}", book1.title, book1.pages, book1.price);
}
  • Here, the name title, pages, and price clearly describe what each value represents.

2) Tuple Structs: Tuple structs look like regular tuples, but they are given a custom type name. The field doesn’t have names; they are accessed by their position index.

Suppose you want to represent colors using three numbers (RGB). Instead of naming each field, you can just group them. For example:

struct Color(u8, u8, u8);

fn main() {
let red = Color(255, 0, 0);
println!("Red color values: {}, {}, {}", red.0, red.1, red.2);
}
  • Here, Color(255, 0, 0) represents the RGB values for red. Tuple structs are useful when naming each field separately doesn’t add much value.

3) Unit-like Structs: Sometimes, you may not need to store any data at all; you just want to create a type that can act as a marker or tag. For this reason, unit-like structs are used.

For example:

struct Logger;

fn main() {
let log = Logger; // Just an instance, holds no data
println!("Logger struct created!");
}

How To Use Named Field Structs In Rust?

A Named Field Struct is a way to group related data together, where each piece of data has a clear name. This makes our code easier to read and understand.

Imagine, it’s like a “profile-card” that has a name, an age, and a status like student or not. Each of these is a field inside the struct. You can then create instances (actual “cards”) with specific values and access their data easily.

Example:

// Defining a Named Field Struct
struct Car {
brand: String,
year: u32,
is_electric: bool,
}

fn main() {
// Creating an instance of the struct
let my_car = Car {
brand: String::from("Tesla"),
year: 2023,
is_electric: true,
};

// Accessing fields
println!("Brand: {}", my_car.brand);
println!("Year: {}", my_car.year);
println!("Is Electric: {}", my_car.is_electric);
}

Output:

Brand: Tesla
Year: 2023
Is Electric: true

Explanation of this code:

  • struct Car { . . . } → Defines a new data type called Car with three named fields: brand, year, and is_electric.
  • let my_car = Car { . . . }; → Creates a specific car instance with actual values.
  • my_car.brand → Accesses the brand field of this car.

How To Use Tuple Structs In Rust?

A Tuple Struct is a way to group related data without giving names to each field, but the struct itself has a name.

Example:

// Defining a Tuple Struct
struct Point(i32, i32, i32);

fn main() {
// Creating an instance of the tuple struct
let my_point = Point(10, 20, 30);

// Accessing values using index
println!("X: {}", my_point.0);
println!("Y: {}", my_point.1);
println!("Z: {}", my_point.2);

// Using the values in a calculation
let sum = my_point.0 + my_point.1 + my_point.2;
println!("Sum of coordinates: {}", sum);
}

Output:

X: 10
Y: 20
Z: 30
Sum of coordinates: 60

Explanation:

  • struct Point(i32, i32, i32); → Defines a new type Point with three fields (all integers), but no field names.
  • let my_point = Point(10, 20, 30); → Creates an instance of Point with values 10, 20, 30.
  • my_point.0, my_point.1, my_point.2 → Access fields by their index.

How To Use Unit-like Structs In Rust?

A Unit-like Struct is a struct that has no fields at all. It doesn’t store any data.

They are especially useful when you want to:

  • Create a unique type to distinguish values.
  • Implement traits on something that doesn’t need data.

Example:

// Defining a Unit-like Struct
struct Logger;

fn main() {
// Creating an instance
let log = Logger;

// Using it to indicate a behavior
print_message(log);
}

// Function that accepts Logger type
fn print_message(_: Logger) {
println!("This is a special logger type!");
}

Output:

This is a special logger type!

Struct Updates Using Struct Update Syntax

Rust allows you to create a new instance of a struct by copying fields from another instance using the . . syntax.

Example:

// Define a Person struct
struct Person {
name: String,
age: u32,
is_student: bool,
}

fn main() {
// Original person
let person1 = Person {
name: String::from("John"),
age: 22,
is_student: true,
};

// Create a new person using struct update syntax
let person2 = Person {
name: String::from("Emma"), // only name is changed
..person1 // rest of the fields copied from person1
};

println!("Person2 Details: Name: {}, Age: {}, Is Student: {}",
person2.name, person2.age, person2.is_student);
}

Output:

Person2 Details: Name: Emma, Age: 22, Is Student: true

Methods in Structs

In Rust, methods let you attach functions directly to a struct. This allows the struct to do actions related to itself, instead of writing separate functions.

Methods help organize code and make it more readable and structured.

impl block → Where you define all methods for a struct.

&self → Means the method can read data from the struct without taking ownership.

Example:

// Define a Rectangle struct
struct Rectangle {
width: u32,
height: u32,
}

// Implement methods for Rectangle
impl Rectangle {
// Method to calculate area
fn area(&self) -> u32 {
self.width * self.height
}

// Method to calculate perimeter
fn perimeter(&self) -> u32 {
2 * (self.width + self.height)
}
}

fn main() {
let my_rect = Rectangle {
width: 15,
height: 25,
};

println!("Rectangle Area: {}", my_rect.area());
println!("Rectangle Perimeter: {}", my_rect.perimeter());
}

Output:

Rectangle Area: 375
Rectangle Perimeter: 80

Exercise: Book Library Struct

create a small library system. Each book has a title, author, and number of pages.

Your task:

  1. Create a struct Book with fields title, author, and pages.
  2. Add a method is_long that returns true if the book has more than 300 pages.
  3. Add another method summary that prints the title and author in a sentence.
  4. Create at least two books and test the methods.
// Define the Book struct
struct Book {
title: String,
author: String,
pages: u32,
}

// Implement methods for Book
impl Book {
// Method to check if book is long
fn is_long(&self) -> bool {
self.pages > 300
}

// Method to print a short summary
fn summary(&self) {
println!("'{}' is written by {}.", self.title, self.author);
}
}

fn main() {
let book1 = Book {
title: String::from("Rust Programming Basics"),
author: String::from("Alice"),
pages: 250,
};

let book2 = Book {
title: String::from("Mastering Rust"),
author: String::from("Bob"),
pages: 400,
};

// Test methods
book1.summary();
println!("Is it a long book? {}\n", book1.is_long());

book2.summary();
println!("Is it a long book? {}", book2.is_long());
}

Output:

'Rust Programming Basics' is written by Alice.
Is it a long book? false

'Mastering Rust' is written by Bob.
Is it a long book? true

Learn More About Rust Programming

Leave a Comment