Hello there, welcome to the very first lesson of this course on Fundamental Rust Concepts for Design Patterns! In this starting lesson, we will dive into the concept of structs in Rust. Structs, or structures, play a vital role in Rust's ability to organize data efficiently, allowing you to create custom data types that enhance code readability and maintain a clean architecture. Structs help form the building blocks for complex systems in Rust, and understanding them will pave the way for mastering more advanced design patterns later in the course.
Let's start by defining a struct in Rust. A struct is essentially a way to group related data together. Think of it as creating a new data type that can have multiple pieces of data, called fields, associated with it.
Here's how you can define a simple struct to represent a Point
in a 2D space:
Rust1struct Point { 2 x: i32, 3 y: i32, 4}
In this example:
struct Point
defines a new struct namedPoint
.- Inside the curly braces
{}
, we define the fieldsx
andy
, both of which are of typei32
, representing integer coordinates of the point in a 2-dimensional plane.
Rust also supports tuple structs, which are struct variants without named fields, and unit structs, which have no fields at all and are useful when implementing traits:
Rust1// Tuple struct 2struct Color(i32, i32, i32); // RGB color 3let black = Color(0, 0, 0); 4 5// Unit structs 6struct Idle; // System is in idle state 7struct Active; // System is in active state
To create an instance of our Point
struct:
Rust1let point = Point { x: 3, y: 4 };
You can access struct fields using dot notation:
Rust1println!("Coordinates: ({}, {})", point.x, point.y);
Rust also provides a convenient struct update syntax to create a new instance based on an existing one:
Rust1let point2 = Point { x: 5, ..point }; // y will be 4
To extend the functionality of structs, Rust allows you to implement both methods and associated functions. Both are defined within impl
blocks, but they serve different purposes:
- Methods are functions that operate on an instance of the struct and take
self
as their first parameter, which is used to access the struct's data. - Associated functions are functions that don't take
self
as a parameter: they're associated with the type itself rather than any particular instance. For example, constructors are usually implemented as associated functions.
Let's see both in action with our Point
struct:
Rust1impl Point { 2 // Associated function (similar to static methods in other languages) 3 // Called using Point::new(x, y) 4 fn new(x: i32, y: i32) -> Self { 5 Self { x, y } 6 } 7 8 // Method that operates on an instance (like instance methods in other languages) 9 // Called using point.distance_from_origin() 10 fn distance_from_origin(&self) -> f64 { 11 ((self.x.pow(2) + self.y.pow(2)) as f64).sqrt() 12 } 13}
In this implementation:
new
is an associated function called usingPoint::new(x, y)
that creates and returns an instance ofPoint
. It doesn't need an instance to work with, which is why it doesn't takeself
. Please note that usingnew
as a constructor is a common convention in Rust, but you can actually use any name for associated functions that create instances.distance_from_origin
is a method that takes&self
(a reference to the instance). It can access the instance's data throughself.x
andself.y
.Self
is an alias for the type in theimpl
block (in this case,Point
).
Methods can take self
in three different ways:
self
: Takes ownership of the instance, giving the method full control but consuming the instance&self
: Immutably borrows the instance, allowing the method to read but not modify the data&mut self
: Mutably borrows the instance, allowing the method to both read and modify the data
In our example, distance_from_origin
only needs to read the values, so it borrows self
immutably with &self
If these terms about ownership and borrowing seem a bit confusing right now, don't worry! We'll be covering these core Rust concepts in an upcoming lesson.
Here's how you can use the Point
struct and the methods we've implemented in a Rust program:
Rust1fn main() { 2 let point = Point::new(3, 4); 3 println!("Distance from origin: {}", point.distance_from_origin()); 4} // Output: Distance from origin: 5
In this program:
- We create a
Point
instance using thenew
method. - We calculate the distance from the origin using
distance_from_origin
. - The
println!
macro is then used to display the result.
This demonstrates how using structs and methods effectively can help structure and simplify your program logic.
In this lesson, we explored how to define and use structs in Rust, including creating instances and implementing methods. We covered regular structs, tuple structs, and unit structs, along with field access and update syntax. We used a Point
struct to illustrate these concepts, showing how to encapsulate data and functionality within a structured format. This foundational understanding prepares you to tackle more complex data structures and design patterns in future lessons.
As you move on to the practice exercises, focus on applying these concepts by creating and manipulating your own structs. These exercises will reinforce your understanding and help you become more comfortable using structs in your Rust programs. Good luck, and enjoy the journey of mastering Rust design patterns!