Introduction
Structs in Rust are a way to create custom data types that group together related pieces of data. This tutorial will introduce you to the basics of structs in Rust, including defining, instantiating, and accessing their data. By the end of this tutorial, you'll have a solid understanding of how to use structs in your Rust projects.
Structs
To define a struct, use the struct
keyword followed by the name of the struct and its fields within curly braces {}
. Each field has a name and a type, separated by a colon:
struct User {
username: String,
email: String,
age: u8,
active: bool,
}
In this example, we've defined a User struct with four fields: username
, email
, age
, and active
.
Creating Instances of a Struct
To create an instance of a struct, specify the name of the struct followed by the field values inside curly braces {}
:
let user1 = User {
username: String::from("Alice"),
email: String::from("[email protected]"),
age: 30,
active: true,
};
Here, we've created an instance of the User struct called user1 with the specified field values.
Accessing Struct Fields
To access the value of a field in a struct, use the dot notation:
println!("Username: {}", user1.username);
println!("Email: {}", user1.email);
println!("Age: {}", user1.age);
println!("Active: {}", user1.active);
This will output the following:
Username: Alice
Email: alice@example.com
Age: 30
Active: true
Mutable Struct Instances
By default, struct instances are immutable. To make a mutable instance, use the mut
keyword:
let mut user2 = User {
username: String::from("Bob"),
email: String::from("[email protected]"),
age: 25,
active: false,
};
To update a field's value in a mutable struct, use the dot notation:
user2.age = 26;
println!("Updated Age: {}", user2.age);
Output:
Updated Age: 26
Using Functions with Structs
Functions can accept and return struct instances. Here's an example of a function that takes a User struct and returns a String:
fn format_user(user: &User) -> String {
format!("{} ({}) - {}", user.username, user.age, user.email)
}
let user_info = format_user(&user1);
println!("{}", user_info);
This will output:
Alice (30) - alice@example.com
Implementations
Implementations allow you to define methods and associated functions for a struct. Use the impl keyword followed by the name of the struct and a block containing the method and associated function definitions.
struct User {
username: String,
email: String,
age: u8,
active: bool,
}
impl User {
// Method and associated function definitions go here
}
Methods:
Methods are functions associated with instances of a struct. To define a method, write a function within the impl block. The first parameter of a method is always self, which represents the instance the method is called on. Here's an example of a method that returns a formatted string for a User instance:
impl User {
fn format_info(&self) -> String {
format!("{} ({}) - {}", self.username, self.age, self.email)
}
}
To call a method, use the dot notation:
let user1 = User {
username: String::from("Alice"),
email: String::from("[email protected]"),
age: 30,
active: true,
};
let user_info = user1.format_info();
println!("{}", user_info);
This will output:
Alice (30) - alice@example.com
Associated Functions
Associated functions are similar to methods, but they don't have an instance of the struct as their first parameter. They are often used as constructors to create new instances of the struct. To define an associated function, write a function within the impl
block without the self
parameter:
impl User {
fn new(username: String, email: String, age: u8) -> Self {
User {
username,
email,
age,
active: true,
}
}
}
To call an associated function, use the double colon notation:
let user2 = User::new(
String::from("Bob"),
String::from("[email protected]"),
25,
);
println!("{}", user2.format_info());
This will output:
Bob (25) - bob@example.com
Implementing Traits for Structs
Traits are a way to define shared behavior between different structs in Rust. By implementing a trait for a struct, you can provide a common interface for different data types. Here's an example of implementing the std::fmt::Display
trait for the User
struct:
use std::fmt;
impl fmt::Display for User {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} ({}) - {}", self.username, self.age, self.email)
}
}
Now you can use the User struct with any function or macro that requires the Display trait, like println!():
println!("{}", user1);
This will output:
Alice (30) - alice@example.com
Conclusion
In this tutorial, you've learned the basics of working with structs in Rust, including defining, creating, and modifying struct instances, as well as using functions with structs. Structs are a powerful way to group related data together and make your code more organized and maintainable. As you continue to explore Rust, you'll find that structs are a fundamental building block for creating complex data structures and handling real-world problems.
You've also learned how to extend structs in Rust with implementations, methods, and associated functions. Additionally, you've discovered how to implement traits for structs, allowing you to define shared behavior between different data types. These techniques will enable you to create more complex and powerful data structures in Rust while keeping your code organized and maintainable.
Happy coding!