INFO

This post needs refactoring to syntax-highlighting

Hi everyone. Before that, I wrote a post called A Slice of Rust: Working with the Slice Type.

Today I’ll try to explain the structs in Rust

Before starting, I’ll create a project with cargo;

cargo new defining_and_instantiating_structs

cd defining_and_instantiating_structs

Rust Programming Language

Introduction

Have you worked with C like languages before? For example C’s itself or C#. These languages have a keyword called struct. This keyword helps you to store different types of data in one place. For example, you created a struct called Student, now we can say we have a new data type. Just an example.

In this post, you’ll see C like struct and tuple structs.

Defining A Struct in Rust

Struct
Image from Real Life Programming

When we creating a struct in Rust, we use the struct keyword firstly. After that, the struct name is coming.

struct OrderItem { 
    // struct body 
}

In the above example, we’ve created a struct called Basket. This will store order information. For example, this is a part of an e-commerce website.

Actually, we haven’t completed it yet. This struct should have fields. Because when you add a product to the basket, it actually adds other product specific information. The product name, quantity, price, etc. Let’s add these fields to our struct.

struct OrderItem {
    product_name: String,
    price: f64,
    quantity: f64,
    user_id: u32
}

It looks good. Now, we have some thoughts about our struct. It will store a product name, price, quantity, and user id.

Can’t We Do That Without Structs?

Of course, you can but. Let’s say you have too many arguments and you pass them to a function. It should be annoying for a developer. Of course, structs didn’t implement only for this approach.

Initializing a Struct

Now, we have a struct, but we don’t know how we can initialize it. How we can create an instance from it? Let’s see

let product_name = String::from("Algorithms");

let order = OrderItem {
    product_name: product_name,
    price: 60.0,
    quantity: 1.0,
    user_id: 12851
};

We initialized our struct this way. As you see, we use key-value pairs. In the above example, we created an immutable struct. This means, once you create an immutable struct, you can’t change it later. To make it mutable, use mut keyword

let mut order = OrderItem {
    product_name: product_name,
    price: 60.0,
    quantity: 1.0,
    user_id: 12851
};

You can’t make fields mutable in a struct. Rust doesn’t allows to you do that. Instead of that, make structs mutable.

Initializing Fields with Shorthand

When your fields and variable names are the same, you can initialize fields without key-pair notation. Because variable names and field names are exactly the same. Let’s see how we can do that;

let product_name = String::from("Algorithms");
let price: u64 = 60.0;
let quantity: u64 = 1.0;

let order = OrderItem {
    product_name: product_name,
    price,
    quantity,
    user_id: 12851
};

The Struct Update Syntax

You can update your struct using the other one;

let order2 = OrderItem {
    product_name: String::from("Rust by Example"),
    ..order
};

println!("Order detail {:?}", order2);

In the above example, you update your struct’s fields using first order. It’s often useful to create a new instance of a struct that uses most of an old instance’s values but changes some. If you used JavaScript before, you should be familiar with this syntax.

Example Usage of Structs

We know how we can create a struct and initialize it. We also know about functions. Let’s use them together.

fn build_order_item(product_name: String, price: f64, quantity: f64, user_id: u32) -> OrderItem {
    OrderItem {
        product_name,
        price,
        quantity,
        user_id
    }
}

In the above example, we created a function called build_order_item. It takes four parameters. It returns OrderItem. Let’s use it 🙂


let mut basket : Vec<&OrderItem> = Vec::new();

let product_name = String::from("Rust by Example");

let order_item = build_order_item(product_name, 45.0, 2.0, 19241);

basket.push(&order_item);

We’ve created a vector. I won’t talk about this for now. But this is a kind of list. C# or Java programmers know that. In C#, you can create a list like that;

List<OrderItem> basket = new List<OrderItem>();

basket.add(/* your code*/);

Okay, we create a function for example, and it returns an OrderItem. When we get a value from this function, we add it to the basket.

Tuple Structs

As we mentioned before, you can also create tuple structs in Rust. Let’s see

struct Color(i32, i32, i32);

And we can use it like that;

let rgb_color = Color(250, 128, 114); // Salmon

As you remember, we discussed tuples in A Trip to Data Types in Rust section. So, we know how we can access a tuple value.

println!("First color is {}", rgb_color.0);

Or we can destruct our struct;

let Color(red, green, blue) = Color(250, 128, 114);

println!("Red is {} Green is {} Blue is {}", red, green, blue);

Unit Like Structs

You can also structs that don’t have any fields. We called these types of structs as unit-like structs. Unit-like structs can be useful in situations in which you need to implement a trait on some type but don’t have any data that you want to store in the type itself.

A library may ask you to create a structure that implements a certain trait to handle events. If you don’t have any data you need to store in the structure, you can create a unit-like struct.

We’ll it later.

struct User;

And we can use it like that;

fn main() {
  let user = User;
}

That’s all for now. Let me know if there is something wrong.

Thanks for reading!

Resources